[6982] Implemented gmlevel-based command security
[getmangos.git] / src / game / LootMgr.cpp
bloba559b7b6ff3896929bc07ba9f174160f9d7923f5
1 /*
2 * Copyright (C) 2005-2008 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"
27 static Rates const qualityToRate[MAX_ITEM_QUALITY] = {
28 RATE_DROP_ITEM_POOR, // ITEM_QUALITY_POOR
29 RATE_DROP_ITEM_NORMAL, // ITEM_QUALITY_NORMAL
30 RATE_DROP_ITEM_UNCOMMON, // ITEM_QUALITY_UNCOMMON
31 RATE_DROP_ITEM_RARE, // ITEM_QUALITY_RARE
32 RATE_DROP_ITEM_EPIC, // ITEM_QUALITY_EPIC
33 RATE_DROP_ITEM_LEGENDARY, // ITEM_QUALITY_LEGENDARY
34 RATE_DROP_ITEM_ARTIFACT, // ITEM_QUALITY_ARTIFACT
37 LootStore LootTemplates_Creature( "creature_loot_template", "creature entry");
38 LootStore LootTemplates_Disenchant( "disenchant_loot_template", "item disenchant id");
39 LootStore LootTemplates_Fishing( "fishing_loot_template", "area id");
40 LootStore LootTemplates_Gameobject( "gameobject_loot_template", "gameobject entry");
41 LootStore LootTemplates_Item( "item_loot_template", "item entry");
42 LootStore LootTemplates_Milling( "milling_loot_template", "item entry");
43 LootStore LootTemplates_Pickpocketing("pickpocketing_loot_template","creature pickpocket lootid");
44 LootStore LootTemplates_Prospecting( "prospecting_loot_template", "item entry");
45 LootStore LootTemplates_QuestMail( "quest_mail_loot_template", "quest id");
46 LootStore LootTemplates_Reference( "reference_loot_template", "reference id");
47 LootStore LootTemplates_Skinning( "skinning_loot_template", "creature skinning id");
49 class LootTemplate::LootGroup // A set of loot definitions for items (refs are not allowed)
51 public:
52 void AddEntry(LootStoreItem& item); // Adds an entry to the group (at loading stage)
53 bool HasQuestDrop() const; // True if group includes at least 1 quest drop entry
54 bool HasQuestDropForPlayer(Player const * player) const;
55 // The same for active quests of the player
56 void Process(Loot& loot) const; // Rolls an item from the group (if any) and adds the item to the loot
57 float RawTotalChance() const; // Overall chance for the group (without equal chanced items)
58 float TotalChance() const; // Overall chance for the group
60 void Verify(LootStore const& lootstore, uint32 id, uint32 group_id) const;
61 void CollectLootIds(LootIdSet& set) const;
62 void CheckLootRefs(LootIdSet* ref_set) const;
63 private:
64 LootStoreItemList ExplicitlyChanced; // Entries with chances defined in DB
65 LootStoreItemList EqualChanced; // Zero chances - every entry takes the same chance
67 LootStoreItem const * Roll() const; // Rolls an item from the group, returns NULL if all miss their chances
70 //Remove all data and free all memory
71 void LootStore::Clear()
73 for (LootTemplateMap::const_iterator itr=m_LootTemplates.begin(); itr != m_LootTemplates.end(); ++itr)
74 delete itr->second;
75 m_LootTemplates.clear();
78 // Checks validity of the loot store
79 // Actual checks are done within LootTemplate::Verify() which is called for every template
80 void LootStore::Verify() const
82 for (LootTemplateMap::const_iterator i = m_LootTemplates.begin(); i != m_LootTemplates.end(); ++i )
83 i->second->Verify(*this, i->first);
86 // Loads a *_loot_template DB table into loot store
87 // All checks of the loaded template are called from here, no error reports at loot generation required
88 void LootStore::LoadLootTable()
90 LootTemplateMap::iterator tab;
91 uint32 count = 0;
93 // Clearing store (for reloading case)
94 Clear();
96 sLog.outString( "%s :", GetName());
98 // 0 1 2 3 4 5 6 7 8
99 QueryResult *result = WorldDatabase.PQuery("SELECT entry, item, ChanceOrQuestChance, groupid, mincountOrRef, maxcount, lootcondition, condition_value1, condition_value2 FROM %s",GetName());
101 if (result)
103 barGoLink bar(result->GetRowCount());
107 Field *fields = result->Fetch();
108 bar.step();
110 uint32 entry = fields[0].GetUInt32();
111 uint32 item = fields[1].GetUInt32();
112 float chanceOrQuestChance = fields[2].GetFloat();
113 uint8 group = fields[3].GetUInt8();
114 int32 mincountOrRef = fields[4].GetInt32();
115 uint8 maxcount = fields[5].GetUInt8();
116 ConditionType condition = (ConditionType)fields[6].GetUInt8();
117 uint32 cond_value1 = fields[7].GetUInt32();
118 uint32 cond_value2 = fields[8].GetUInt32();
120 if(!PlayerCondition::IsValid(condition,cond_value1, cond_value2))
122 sLog.outErrorDb("... in table '%s' entry %u item %u", GetName(), entry, item);
123 continue; // error already printed to log/console.
126 // (condition + cond_value1/2) are converted into single conditionId
127 uint16 conditionId = objmgr.GetConditionId(condition, cond_value1, cond_value2);
129 LootStoreItem storeitem = LootStoreItem(item, chanceOrQuestChance, group, conditionId, mincountOrRef, maxcount);
131 if (!storeitem.IsValid(*this,entry)) // Validity checks
132 continue;
134 // Looking for the template of the entry
135 // often entries are put together
136 if (m_LootTemplates.empty() || tab->first != entry)
138 // Searching the template (in case template Id changed)
139 tab = m_LootTemplates.find(entry);
140 if ( tab == m_LootTemplates.end() )
142 std::pair< LootTemplateMap::iterator, bool > pr = m_LootTemplates.insert(LootTemplateMap::value_type(entry, new LootTemplate));
143 tab = pr.first;
146 // else is empty - template Id and iter are the same
147 // finally iter refers to already existed or just created <entry, LootTemplate>
149 // Adds current row to the template
150 tab->second->AddEntry(storeitem);
151 ++count;
153 } while (result->NextRow());
155 delete result;
157 Verify(); // Checks validity of the loot store
159 sLog.outString();
160 sLog.outString( ">> Loaded %u loot definitions (%d templates)", count, m_LootTemplates.size());
162 else
164 sLog.outString();
165 sLog.outErrorDb( ">> Loaded 0 loot definitions. DB table `%s` is empty.",GetName() );
169 bool LootStore::HaveQuestLootFor(uint32 loot_id) const
171 LootTemplateMap::const_iterator itr = m_LootTemplates.find(loot_id);
172 if(itr == m_LootTemplates.end())
173 return false;
175 // scan loot for quest items
176 return itr->second->HasQuestDrop(m_LootTemplates);
179 bool LootStore::HaveQuestLootForPlayer(uint32 loot_id,Player* player) const
181 LootTemplateMap::const_iterator tab = m_LootTemplates.find(loot_id);
182 if (tab != m_LootTemplates.end())
183 if (tab->second->HasQuestDropForPlayer(m_LootTemplates, player))
184 return true;
186 return false;
189 LootTemplate const* LootStore::GetLootFor(uint32 loot_id) const
191 LootTemplateMap::const_iterator tab = m_LootTemplates.find(loot_id);
193 if (tab == m_LootTemplates.end())
194 return NULL;
196 return tab->second;
199 void LootStore::LoadAndCollectLootIds(LootIdSet& ids_set)
201 LoadLootTable();
203 for(LootTemplateMap::const_iterator tab = m_LootTemplates.begin(); tab != m_LootTemplates.end(); ++tab)
204 ids_set.insert(tab->first);
207 void LootStore::CheckLootRefs(LootIdSet* ref_set) const
209 for(LootTemplateMap::const_iterator ltItr = m_LootTemplates.begin(); ltItr != m_LootTemplates.end(); ++ltItr)
210 ltItr->second->CheckLootRefs(ref_set);
213 void LootStore::ReportUnusedIds(LootIdSet const& ids_set) const
215 // all still listed ids isn't referenced
216 for(LootIdSet::const_iterator itr = ids_set.begin(); itr != ids_set.end(); ++itr)
217 sLog.outErrorDb("Table '%s' entry %d isn't %s and not referenced from loot, and then useless.", GetName(), *itr,GetEntryName());
220 void LootStore::ReportNotExistedId(uint32 id) const
222 sLog.outErrorDb("Table '%s' entry %d (%s) not exist but used as loot id in DB.", GetName(), id,GetEntryName());
226 // --------- LootStoreItem ---------
229 // Checks if the entry (quest, non-quest, reference) takes it's chance (at loot generation)
230 // RATE_DROP_ITEMS is no longer used for all types of entries
231 bool LootStoreItem::Roll() const
233 if(chance>=100.f)
234 return true;
236 if(mincountOrRef < 0) // reference case
237 return roll_chance_f(chance*sWorld.getRate(RATE_DROP_ITEM_REFERENCED));
239 ItemPrototype const *pProto = objmgr.GetItemPrototype(itemid);
241 float qualityModifier = pProto ? sWorld.getRate(qualityToRate[pProto->Quality]) : 1.0f;
243 return roll_chance_f(chance*qualityModifier);
246 // Checks correctness of values
247 bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const
249 if (mincountOrRef == 0)
251 sLog.outErrorDb("Table '%s' entry %d item %d: wrong mincountOrRef (%d) - skipped", store.GetName(), entry, itemid, mincountOrRef);
252 return false;
255 if( mincountOrRef > 0 ) // item (quest or non-quest) entry, maybe grouped
257 ItemPrototype const *proto = objmgr.GetItemPrototype(itemid);
258 if(!proto)
260 sLog.outErrorDb("Table '%s' entry %d item %d: item entry not listed in `item_template` - skipped", store.GetName(), entry, itemid);
261 return false;
264 if( chance == 0 && group == 0) // Zero chance is allowed for grouped entries only
266 sLog.outErrorDb("Table '%s' entry %d item %d: equal-chanced grouped entry, but group not defined - skipped", store.GetName(), entry, itemid);
267 return false;
270 if( chance != 0 && chance < 0.000001f ) // loot with low chance
272 sLog.outErrorDb("Table '%s' entry %d item %d: low chance (%f) - skipped",
273 store.GetName(), entry, itemid, chance);
274 return false;
277 else // mincountOrRef < 0
279 if (needs_quest)
280 sLog.outErrorDb("Table '%s' entry %d item %d: quest chance will be treated as non-quest chance", store.GetName(), entry, itemid);
281 else if( chance == 0 ) // no chance for the reference
283 sLog.outErrorDb("Table '%s' entry %d item %d: zero chance is specified for a reference, skipped", store.GetName(), entry, itemid);
284 return false;
287 return true; // Referenced template existence is checked at whole store level
291 // --------- LootItem ---------
294 // Constructor, copies most fields from LootStoreItem and generates random count
295 LootItem::LootItem(LootStoreItem const& li)
297 itemid = li.itemid;
298 conditionId = li.conditionId;
300 ItemPrototype const* proto = objmgr.GetItemPrototype(itemid);
301 freeforall = proto && (proto->Flags & ITEM_FLAGS_PARTY_LOOT);
303 needs_quest = li.needs_quest;
305 count = urand(li.mincountOrRef, li.maxcount); // constructor called for mincountOrRef > 0 only
306 randomSuffix = GenerateEnchSuffixFactor(itemid);
307 randomPropertyId = Item::GenerateItemRandomPropertyId(itemid);
308 is_looted = 0;
309 is_blocked = 0;
310 is_underthreshold = 0;
311 is_counted = 0;
314 // Basic checks for player/item compatibility - if false no chance to see the item in the loot
315 bool LootItem::AllowedForPlayer(Player const * player) const
317 // DB conditions check
318 if ( !objmgr.IsPlayerMeetToCondition(player,conditionId) )
319 return false;
321 if ( needs_quest )
323 // Checking quests for quest-only drop (check only quests requirements in this case)
324 if( !player->HasQuestForItem(itemid) )
325 return false;
327 else
329 // Not quest only drop (check quest starting items for already accepted non-repeatable quests)
330 ItemPrototype const *pProto = objmgr.GetItemPrototype(itemid);
331 if (pProto && pProto->StartQuest && player->GetQuestStatus(pProto->StartQuest) != QUEST_STATUS_NONE && !player->HasQuestForItem(itemid))
332 return false;
335 return true;
339 // --------- Loot ---------
342 // Inserts the item into the loot (called by LootTemplate processors)
343 void Loot::AddItem(LootStoreItem const & item)
345 if (item.needs_quest) // Quest drop
347 if (quest_items.size() < MAX_NR_QUEST_ITEMS)
348 quest_items.push_back(LootItem(item));
350 else if (items.size() < MAX_NR_LOOT_ITEMS) // Non-quest drop
352 items.push_back(LootItem(item));
354 // non-conditional one-player only items are counted here,
355 // free for all items are counted in FillFFALoot(),
356 // non-ffa conditionals are counted in FillNonQuestNonFFAConditionalLoot()
357 if( !item.conditionId )
359 ItemPrototype const* proto = objmgr.GetItemPrototype(item.itemid);
360 if( !proto || (proto->Flags & ITEM_FLAGS_PARTY_LOOT)==0 )
361 ++unlootedCount;
366 // Calls processor of corresponding LootTemplate (which handles everything including references)
367 void Loot::FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner)
369 LootTemplate const* tab = store.GetLootFor(loot_id);
371 if (!tab)
373 sLog.outErrorDb("Table '%s' loot id #%u used but it doesn't have records.",store.GetName(),loot_id);
374 return;
377 items.reserve(MAX_NR_LOOT_ITEMS);
378 quest_items.reserve(MAX_NR_QUEST_ITEMS);
380 tab->Process(*this, store); // Processing is done there, callback via Loot::AddItem()
382 // Setting access rights fow group-looting case
383 if(!loot_owner)
384 return;
385 Group * pGroup=loot_owner->GetGroup();
386 if(!pGroup)
387 return;
388 for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
390 //fill the quest item map for every player in the recipient's group
391 Player* pl = itr->getSource();
392 if(!pl)
393 continue;
394 uint32 plguid = pl->GetGUIDLow();
395 QuestItemMap::iterator qmapitr = PlayerQuestItems.find(plguid);
396 if (qmapitr == PlayerQuestItems.end())
398 FillQuestLoot(pl);
400 qmapitr = PlayerFFAItems.find(plguid);
401 if (qmapitr == PlayerFFAItems.end())
403 FillFFALoot(pl);
405 qmapitr = PlayerNonQuestNonFFAConditionalItems.find(plguid);
406 if (qmapitr == PlayerNonQuestNonFFAConditionalItems.end())
408 FillNonQuestNonFFAConditionalLoot(pl);
413 QuestItemList* Loot::FillFFALoot(Player* player)
415 QuestItemList *ql = new QuestItemList();
417 for(uint8 i = 0; i < items.size(); i++)
419 LootItem &item = items[i];
420 if(!item.is_looted && item.freeforall && item.AllowedForPlayer(player) )
422 ql->push_back(QuestItem(i));
423 ++unlootedCount;
426 if (ql->empty())
428 delete ql;
429 return NULL;
432 PlayerFFAItems[player->GetGUIDLow()] = ql;
433 return ql;
436 QuestItemList* Loot::FillQuestLoot(Player* player)
438 if (items.size() == MAX_NR_LOOT_ITEMS) return NULL;
439 QuestItemList *ql = new QuestItemList();
441 for(uint8 i = 0; i < quest_items.size(); i++)
443 LootItem &item = quest_items[i];
444 if(!item.is_looted && item.AllowedForPlayer(player) )
446 ql->push_back(QuestItem(i));
448 // questitems get blocked when they first apper in a
449 // player's quest vector
451 // increase once if one looter only, looter-times if free for all
452 if (item.freeforall || !item.is_blocked)
453 ++unlootedCount;
455 item.is_blocked = true;
457 if (items.size() + ql->size() == MAX_NR_LOOT_ITEMS)
458 break;
461 if (ql->empty())
463 delete ql;
464 return NULL;
467 PlayerQuestItems[player->GetGUIDLow()] = ql;
468 return ql;
471 QuestItemList* Loot::FillNonQuestNonFFAConditionalLoot(Player* player)
473 QuestItemList *ql = new QuestItemList();
475 for(uint8 i = 0; i < items.size(); ++i)
477 LootItem &item = items[i];
478 if(!item.is_looted && !item.freeforall && item.conditionId && item.AllowedForPlayer(player))
480 ql->push_back(QuestItem(i));
481 if(!item.is_counted)
483 ++unlootedCount;
484 item.is_counted=true;
488 if (ql->empty())
490 delete ql;
491 return NULL;
494 PlayerNonQuestNonFFAConditionalItems[player->GetGUIDLow()] = ql;
495 return ql;
498 //===================================================
500 void Loot::NotifyItemRemoved(uint8 lootIndex)
502 // notify all players that are looting this that the item was removed
503 // convert the index to the slot the player sees
504 std::set<uint64>::iterator i_next;
505 for(std::set<uint64>::iterator i = PlayersLooting.begin(); i != PlayersLooting.end(); i = i_next)
507 i_next = i;
508 ++i_next;
509 if(Player* pl = ObjectAccessor::FindPlayer(*i))
510 pl->SendNotifyLootItemRemoved(lootIndex);
511 else
512 PlayersLooting.erase(i);
516 void Loot::NotifyMoneyRemoved()
518 // notify all players that are looting this that the money was removed
519 std::set<uint64>::iterator i_next;
520 for(std::set<uint64>::iterator i = PlayersLooting.begin(); i != PlayersLooting.end(); i = i_next)
522 i_next = i;
523 ++i_next;
524 if(Player* pl = ObjectAccessor::FindPlayer(*i))
525 pl->SendNotifyLootMoneyRemoved();
526 else
527 PlayersLooting.erase(i);
531 void Loot::NotifyQuestItemRemoved(uint8 questIndex)
533 // when a free for all questitem is looted
534 // all players will get notified of it being removed
535 // (other questitems can be looted by each group member)
536 // bit inefficient but isnt called often
538 std::set<uint64>::iterator i_next;
539 for(std::set<uint64>::iterator i = PlayersLooting.begin(); i != PlayersLooting.end(); i = i_next)
541 i_next = i;
542 ++i_next;
543 if(Player* pl = ObjectAccessor::FindPlayer(*i))
545 QuestItemMap::iterator pq = PlayerQuestItems.find(pl->GetGUIDLow());
546 if (pq != PlayerQuestItems.end() && pq->second)
548 // find where/if the player has the given item in it's vector
549 QuestItemList& pql = *pq->second;
551 uint8 j;
552 for (j = 0; j < pql.size(); ++j)
553 if (pql[j].index == questIndex)
554 break;
556 if (j < pql.size())
557 pl->SendNotifyLootItemRemoved(items.size()+j);
560 else
561 PlayersLooting.erase(i);
565 void Loot::generateMoneyLoot( uint32 minAmount, uint32 maxAmount )
567 if (maxAmount > 0)
569 if (maxAmount <= minAmount)
570 gold = uint32(maxAmount * sWorld.getRate(RATE_DROP_MONEY));
571 else if ((maxAmount - minAmount) < 32700)
572 gold = uint32(urand(minAmount, maxAmount) * sWorld.getRate(RATE_DROP_MONEY));
573 else
574 gold = uint32(urand(minAmount >> 8, maxAmount >> 8) * sWorld.getRate(RATE_DROP_MONEY)) << 8;
578 LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, QuestItem **qitem, QuestItem **ffaitem, QuestItem **conditem)
580 LootItem* item = NULL;
581 bool is_looted = true;
582 if (lootSlot >= items.size())
584 uint32 questSlot = lootSlot - items.size();
585 QuestItemMap::const_iterator itr = PlayerQuestItems.find(player->GetGUIDLow());
586 if (itr != PlayerQuestItems.end() && questSlot < itr->second->size())
588 QuestItem *qitem2 = &itr->second->at(questSlot);
589 if(qitem)
590 *qitem = qitem2;
591 item = &quest_items[qitem2->index];
592 is_looted = qitem2->is_looted;
595 else
597 item = &items[lootSlot];
598 is_looted = item->is_looted;
599 if(item->freeforall)
601 QuestItemMap::const_iterator itr = PlayerFFAItems.find(player->GetGUIDLow());
602 if (itr != PlayerFFAItems.end())
604 for(QuestItemList::iterator iter=itr->second->begin(); iter!= itr->second->end(); ++iter)
605 if(iter->index==lootSlot)
607 QuestItem *ffaitem2 = (QuestItem*)&(*iter);
608 if(ffaitem)
609 *ffaitem = ffaitem2;
610 is_looted = ffaitem2->is_looted;
611 break;
615 else if(item->conditionId)
617 QuestItemMap::const_iterator itr = PlayerNonQuestNonFFAConditionalItems.find(player->GetGUIDLow());
618 if (itr != PlayerNonQuestNonFFAConditionalItems.end())
620 for(QuestItemList::iterator iter=itr->second->begin(); iter!= itr->second->end(); ++iter)
622 if(iter->index==lootSlot)
624 QuestItem *conditem2 = (QuestItem*)&(*iter);
625 if(conditem)
626 *conditem = conditem2;
627 is_looted = conditem2->is_looted;
628 break;
635 if(is_looted)
636 return NULL;
638 return item;
641 ByteBuffer& operator<<(ByteBuffer& b, LootItem const& li)
643 b << uint32(li.itemid);
644 b << uint32(li.count); // nr of items of this type
645 b << uint32(objmgr.GetItemPrototype(li.itemid)->DisplayInfoID);
646 b << uint32(li.randomSuffix);
647 b << uint32(li.randomPropertyId);
648 //b << uint8(0); // slot type - will send after this function call
649 return b;
652 ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
654 Loot &l = lv.loot;
656 uint8 itemsShown = 0;
658 //gold
659 b << uint32(lv.permission!=NONE_PERMISSION ? l.gold : 0);
661 size_t count_pos = b.wpos(); // pos of item count byte
662 b << uint8(0); // item count placeholder
664 switch (lv.permission)
666 case GROUP_PERMISSION:
668 // You are not the items proprietary, so you can only see
669 // blocked rolled items and quest items, and !ffa items
670 for (uint8 i = 0; i < l.items.size(); ++i)
672 if (!l.items[i].is_looted && !l.items[i].freeforall && !l.items[i].conditionId && l.items[i].AllowedForPlayer(lv.viewer))
674 uint8 slot_type = (l.items[i].is_blocked || l.items[i].is_underthreshold) ? 0 : 1;
676 b << uint8(i) << l.items[i]; //send the index and the item if it's not looted, and blocked or under threshold, free for all items will be sent later, only one-player loots here
677 b << uint8(slot_type); // 0 - get 1 - look only
678 ++itemsShown;
681 break;
683 case ALL_PERMISSION:
684 case MASTER_PERMISSION:
686 uint8 slot_type = (lv.permission==MASTER_PERMISSION) ? 2 : 0;
687 for (uint8 i = 0; i < l.items.size(); ++i)
689 if (!l.items[i].is_looted && !l.items[i].freeforall && !l.items[i].conditionId && l.items[i].AllowedForPlayer(lv.viewer))
691 b << uint8(i) << l.items[i]; //only send one-player loot items now, free for all will be sent later
692 b << uint8(slot_type); // 0 - get 2 - master selection
693 ++itemsShown;
696 break;
698 case NONE_PERMISSION:
699 default:
700 return b; // nothing output more
703 if (lv.qlist)
705 for (QuestItemList::iterator qi = lv.qlist->begin() ; qi != lv.qlist->end(); ++qi)
707 LootItem &item = l.quest_items[qi->index];
708 if (!qi->is_looted && !item.is_looted)
710 b << uint8(l.items.size() + (qi - lv.qlist->begin()));
711 b << item;
712 b << uint8(0); // allow loot
713 ++itemsShown;
718 if (lv.ffalist)
720 for (QuestItemList::iterator fi = lv.ffalist->begin() ; fi != lv.ffalist->end(); ++fi)
722 LootItem &item = l.items[fi->index];
723 if (!fi->is_looted && !item.is_looted)
725 b << uint8(fi->index) << item;
726 b << uint8(0); // allow loot
727 ++itemsShown;
732 if (lv.conditionallist)
734 for (QuestItemList::iterator ci = lv.conditionallist->begin() ; ci != lv.conditionallist->end(); ++ci)
736 LootItem &item = l.items[ci->index];
737 if (!ci->is_looted && !item.is_looted)
739 b << uint8(ci->index) << item;
740 b << uint8(0); // allow loot
741 ++itemsShown;
746 //update number of items shown
747 b.put<uint8>(count_pos,itemsShown);
749 return b;
753 // --------- LootTemplate::LootGroup ---------
756 // Adds an entry to the group (at loading stage)
757 void LootTemplate::LootGroup::AddEntry(LootStoreItem& item)
759 if (item.chance != 0)
760 ExplicitlyChanced.push_back(item);
761 else
762 EqualChanced.push_back(item);
765 // Rolls an item from the group, returns NULL if all miss their chances
766 LootStoreItem const * LootTemplate::LootGroup::Roll() const
768 if (!ExplicitlyChanced.empty()) // First explicitly chanced entries are checked
770 float Roll = rand_chance();
772 for (uint32 i=0; i<ExplicitlyChanced.size(); ++i) //check each explicitly chanced entry in the template and modify its chance based on quality.
774 if(ExplicitlyChanced[i].chance>=100.f)
775 return &ExplicitlyChanced[i];
777 ItemPrototype const *pProto = objmgr.GetItemPrototype(ExplicitlyChanced[i].itemid);
778 float qualityMultiplier = pProto ? sWorld.getRate(qualityToRate[pProto->Quality]) : 1.0f;
779 Roll -= ExplicitlyChanced[i].chance * qualityMultiplier;
780 if (Roll < 0)
781 return &ExplicitlyChanced[i];
784 if (!EqualChanced.empty()) // If nothing selected yet - an item is taken from equal-chanced part
785 return &EqualChanced[irand(0, EqualChanced.size()-1)];
787 return NULL; // Empty drop from the group
790 // True if group includes at least 1 quest drop entry
791 bool LootTemplate::LootGroup::HasQuestDrop() const
793 for (LootStoreItemList::const_iterator i=ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i)
794 if (i->needs_quest)
795 return true;
796 for (LootStoreItemList::const_iterator i=EqualChanced.begin(); i != EqualChanced.end(); ++i)
797 if (i->needs_quest)
798 return true;
799 return false;
802 // True if group includes at least 1 quest drop entry for active quests of the player
803 bool LootTemplate::LootGroup::HasQuestDropForPlayer(Player const * player) const
805 for (LootStoreItemList::const_iterator i=ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i)
806 if (player->HasQuestForItem(i->itemid))
807 return true;
808 for (LootStoreItemList::const_iterator i=EqualChanced.begin(); i != EqualChanced.end(); ++i)
809 if (player->HasQuestForItem(i->itemid))
810 return true;
811 return false;
814 // Rolls an item from the group (if any takes its chance) and adds the item to the loot
815 void LootTemplate::LootGroup::Process(Loot& loot) const
817 LootStoreItem const * item = Roll();
818 if (item != NULL)
819 loot.AddItem(*item);
822 // Overall chance for the group without equal chanced items
823 float LootTemplate::LootGroup::RawTotalChance() const
825 float result = 0;
827 for (LootStoreItemList::const_iterator i=ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i)
828 if ( !i->needs_quest )
829 result += i->chance;
831 return result;
834 // Overall chance for the group
835 float LootTemplate::LootGroup::TotalChance() const
837 float result = RawTotalChance();
839 if (!EqualChanced.empty() && result < 100.0f)
840 return 100.0f;
842 return result;
845 void LootTemplate::LootGroup::Verify(LootStore const& lootstore, uint32 id, uint32 group_id) const
847 float chance = RawTotalChance();
848 if (chance > 101.0f) // TODO: replace with 100% when DBs will be ready
850 sLog.outErrorDb("Table '%s' entry %u group %d has total chance > 100%% (%f)", lootstore.GetName(), id, group_id, chance);
853 if(chance >= 100.0f && !EqualChanced.empty())
855 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);
859 void LootTemplate::LootGroup::CheckLootRefs(LootIdSet* ref_set) const
861 for (LootStoreItemList::const_iterator ieItr=ExplicitlyChanced.begin(); ieItr != ExplicitlyChanced.end(); ++ieItr)
863 if(ieItr->mincountOrRef < 0)
865 if(!LootTemplates_Reference.GetLootFor(-ieItr->mincountOrRef))
866 LootTemplates_Reference.ReportNotExistedId(-ieItr->mincountOrRef);
867 else if(ref_set)
868 ref_set->erase(-ieItr->mincountOrRef);
872 for (LootStoreItemList::const_iterator ieItr=EqualChanced.begin(); ieItr != EqualChanced.end(); ++ieItr)
874 if(ieItr->mincountOrRef < 0)
876 if(!LootTemplates_Reference.GetLootFor(-ieItr->mincountOrRef))
877 LootTemplates_Reference.ReportNotExistedId(-ieItr->mincountOrRef);
878 else if(ref_set)
879 ref_set->erase(-ieItr->mincountOrRef);
885 // --------- LootTemplate ---------
888 // Adds an entry to the group (at loading stage)
889 void LootTemplate::AddEntry(LootStoreItem& item)
891 if (item.group > 0 && item.mincountOrRef > 0) // Group
893 if (item.group >= Groups.size())
894 Groups.resize(item.group); // Adds new group the the loot template if needed
895 Groups[item.group-1].AddEntry(item); // Adds new entry to the group
897 else // Non-grouped entries and references are stored together
898 Entries.push_back(item);
901 // Rolls for every item in the template and adds the rolled items the the loot
902 void LootTemplate::Process(Loot& loot, LootStore const& store, uint8 groupId) const
904 if (groupId) // Group reference uses own processing of the group
906 if (groupId > Groups.size())
907 return; // Error message already printed at loading stage
909 Groups[groupId-1].Process(loot);
910 return;
913 // Rolling non-grouped items
914 for (LootStoreItemList::const_iterator i = Entries.begin() ; i != Entries.end() ; ++i )
916 if ( !i->Roll() )
917 continue; // Bad luck for the entry
919 if (i->mincountOrRef < 0) // References processing
921 LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(-i->mincountOrRef);
923 if(!Referenced)
924 continue; // Error message already printed at loading stage
926 for (uint32 loop=0; loop < i->maxcount; ++loop )// Ref multiplicator
927 Referenced->Process(loot, store, i->group); // Ref processing
929 else // Plain entries (not a reference, not grouped)
930 loot.AddItem(*i); // Chance is already checked, just add
933 // Now processing groups
934 for (LootGroups::const_iterator i = Groups.begin( ) ; i != Groups.end( ) ; ++i )
935 i->Process(loot);
938 // True if template includes at least 1 quest drop entry
939 bool LootTemplate::HasQuestDrop(LootTemplateMap const& store, uint8 groupId) const
941 if (groupId) // Group reference
943 if (groupId > Groups.size())
944 return false; // Error message [should be] already printed at loading stage
945 return Groups[groupId-1].HasQuestDrop();
948 for (LootStoreItemList::const_iterator i = Entries.begin(); i != Entries.end(); ++i )
950 if (i->mincountOrRef < 0) // References
952 LootTemplateMap::const_iterator Referenced = store.find(-i->mincountOrRef);
953 if( Referenced ==store.end() )
954 continue; // Error message [should be] already printed at loading stage
955 if (Referenced->second->HasQuestDrop(store, i->group) )
956 return true;
958 else if ( i->needs_quest )
959 return true; // quest drop found
962 // Now processing groups
963 for (LootGroups::const_iterator i = Groups.begin() ; i != Groups.end() ; ++i )
964 if (i->HasQuestDrop())
965 return true;
967 return false;
970 // True if template includes at least 1 quest drop for an active quest of the player
971 bool LootTemplate::HasQuestDropForPlayer(LootTemplateMap const& store, Player const* player, uint8 groupId) const
973 if (groupId) // Group reference
975 if (groupId > Groups.size())
976 return false; // Error message already printed at loading stage
977 return Groups[groupId-1].HasQuestDropForPlayer(player);
980 // Checking non-grouped entries
981 for (LootStoreItemList::const_iterator i = Entries.begin() ; i != Entries.end() ; ++i )
983 if (i->mincountOrRef < 0) // References processing
985 LootTemplateMap::const_iterator Referenced = store.find(-i->mincountOrRef);
986 if (Referenced == store.end() )
987 continue; // Error message already printed at loading stage
988 if (Referenced->second->HasQuestDropForPlayer(store, player, i->group) )
989 return true;
991 else if ( player->HasQuestForItem(i->itemid) )
992 return true; // active quest drop found
995 // Now checking groups
996 for (LootGroups::const_iterator i = Groups.begin(); i != Groups.end(); ++i )
997 if (i->HasQuestDrop())
998 return true;
1000 return false;
1003 // Checks integrity of the template
1004 void LootTemplate::Verify(LootStore const& lootstore, uint32 id) const
1006 // Checking group chances
1007 for (uint32 i=0; i < Groups.size(); ++i)
1008 Groups[i].Verify(lootstore,id,i+1);
1010 // TODO: References validity checks
1013 void LootTemplate::CheckLootRefs(LootIdSet* ref_set) const
1015 for(LootStoreItemList::const_iterator ieItr = Entries.begin(); ieItr != Entries.end(); ++ieItr)
1017 if(ieItr->mincountOrRef < 0)
1019 if(!LootTemplates_Reference.GetLootFor(-ieItr->mincountOrRef))
1020 LootTemplates_Reference.ReportNotExistedId(-ieItr->mincountOrRef);
1021 else if(ref_set)
1022 ref_set->erase(-ieItr->mincountOrRef);
1026 for(LootGroups::const_iterator grItr = Groups.begin(); grItr != Groups.end(); ++grItr)
1027 grItr->CheckLootRefs(ref_set);
1030 void LoadLootTemplates_Creature()
1032 LootIdSet ids_set, ids_setUsed;
1033 LootTemplates_Creature.LoadAndCollectLootIds(ids_set);
1035 // remove real entries and check existence loot
1036 for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i )
1038 if(CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
1040 if(uint32 lootid = cInfo->lootid)
1042 if(!ids_set.count(lootid))
1043 LootTemplates_Creature.ReportNotExistedId(lootid);
1044 else
1045 ids_setUsed.insert(lootid);
1049 for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr)
1050 ids_set.erase(*itr);
1052 // output error for any still listed (not referenced from appropriate table) ids
1053 LootTemplates_Creature.ReportUnusedIds(ids_set);
1056 void LoadLootTemplates_Disenchant()
1058 LootIdSet ids_set, ids_setUsed;
1059 LootTemplates_Disenchant.LoadAndCollectLootIds(ids_set);
1061 // remove real entries and check existence loot
1062 for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i )
1064 if(ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype>(i))
1066 if(uint32 lootid = proto->DisenchantID)
1068 if(!ids_set.count(lootid))
1069 LootTemplates_Disenchant.ReportNotExistedId(lootid);
1070 else
1071 ids_setUsed.insert(lootid);
1075 for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr)
1076 ids_set.erase(*itr);
1077 // output error for any still listed (not referenced from appropriate table) ids
1078 LootTemplates_Disenchant.ReportUnusedIds(ids_set);
1081 void LoadLootTemplates_Fishing()
1083 LootIdSet ids_set;
1084 LootTemplates_Fishing.LoadAndCollectLootIds(ids_set);
1086 // remove real entries and check existence loot
1087 for(uint32 i = 1; i < sAreaStore.GetNumRows(); ++i )
1089 if(AreaTableEntry const* areaEntry = sAreaStore.LookupEntry(i))
1090 if(ids_set.count(areaEntry->ID))
1091 ids_set.erase(areaEntry->ID);
1094 // output error for any still listed (not referenced from appropriate table) ids
1095 LootTemplates_Fishing.ReportUnusedIds(ids_set);
1098 void LoadLootTemplates_Gameobject()
1100 LootIdSet ids_set, ids_setUsed;
1101 LootTemplates_Gameobject.LoadAndCollectLootIds(ids_set);
1103 // remove real entries and check existence loot
1104 for(uint32 i = 1; i < sGOStorage.MaxEntry; ++i )
1106 if(GameObjectInfo const* gInfo = sGOStorage.LookupEntry<GameObjectInfo>(i))
1108 if(uint32 lootid = GameObject::GetLootId(gInfo))
1110 if(!ids_set.count(lootid))
1111 LootTemplates_Gameobject.ReportNotExistedId(lootid);
1112 else
1113 ids_setUsed.insert(lootid);
1117 for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr)
1118 ids_set.erase(*itr);
1120 // output error for any still listed (not referenced from appropriate table) ids
1121 LootTemplates_Gameobject.ReportUnusedIds(ids_set);
1124 void LoadLootTemplates_Item()
1126 LootIdSet ids_set;
1127 LootTemplates_Item.LoadAndCollectLootIds(ids_set);
1129 // remove real entries and check existence loot
1130 for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i )
1131 if(ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype>(i))
1132 if(ids_set.count(proto->ItemId))
1133 ids_set.erase(proto->ItemId);
1135 // output error for any still listed (not referenced from appropriate table) ids
1136 LootTemplates_Item.ReportUnusedIds(ids_set);
1139 void LoadLootTemplates_Milling()
1141 LootIdSet ids_set;
1142 LootTemplates_Milling.LoadAndCollectLootIds(ids_set);
1144 // remove real entries and check existence loot
1145 for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i )
1146 if(ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype>(i))
1147 if(ids_set.count(proto->ItemId))
1148 ids_set.erase(proto->ItemId);
1150 // output error for any still listed (not referenced from appropriate table) ids
1151 LootTemplates_Milling.ReportUnusedIds(ids_set);
1154 void LoadLootTemplates_Pickpocketing()
1156 LootIdSet ids_set, ids_setUsed;
1157 LootTemplates_Pickpocketing.LoadAndCollectLootIds(ids_set);
1159 // remove real entries and check existence loot
1160 for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i )
1162 if(CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
1164 if(uint32 lootid = cInfo->pickpocketLootId)
1166 if(!ids_set.count(lootid))
1167 LootTemplates_Pickpocketing.ReportNotExistedId(lootid);
1168 else
1169 ids_setUsed.insert(lootid);
1173 for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr)
1174 ids_set.erase(*itr);
1176 // output error for any still listed (not referenced from appropriate table) ids
1177 LootTemplates_Pickpocketing.ReportUnusedIds(ids_set);
1180 void LoadLootTemplates_Prospecting()
1182 LootIdSet ids_set;
1183 LootTemplates_Prospecting.LoadAndCollectLootIds(ids_set);
1185 // remove real entries and check existence loot
1186 for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i )
1187 if(ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype>(i))
1188 if(ids_set.count(proto->ItemId))
1189 ids_set.erase(proto->ItemId);
1191 // output error for any still listed (not referenced from appropriate table) ids
1192 LootTemplates_Prospecting.ReportUnusedIds(ids_set);
1195 void LoadLootTemplates_QuestMail()
1197 LootIdSet ids_set;
1198 LootTemplates_QuestMail.LoadAndCollectLootIds(ids_set);
1200 // remove real entries and check existence loot
1201 ObjectMgr::QuestMap const& questMap = objmgr.GetQuestTemplates();
1202 for(ObjectMgr::QuestMap::const_iterator itr = questMap.begin(); itr != questMap.end(); ++itr )
1203 if(ids_set.count(itr->first))
1204 ids_set.erase(itr->first);
1206 // output error for any still listed (not referenced from appropriate table) ids
1207 LootTemplates_QuestMail.ReportUnusedIds(ids_set);
1210 void LoadLootTemplates_Skinning()
1212 LootIdSet ids_set, ids_setUsed;
1213 LootTemplates_Skinning.LoadAndCollectLootIds(ids_set);
1215 // remove real entries and check existence loot
1216 for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i )
1218 if(CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
1220 if(uint32 lootid = cInfo->SkinLootId)
1222 if(!ids_set.count(lootid))
1223 LootTemplates_Skinning.ReportNotExistedId(lootid);
1224 else
1225 ids_setUsed.insert(lootid);
1229 for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr)
1230 ids_set.erase(*itr);
1232 // output error for any still listed (not referenced from appropriate table) ids
1233 LootTemplates_Skinning.ReportUnusedIds(ids_set);
1236 void LoadLootTemplates_Reference()
1238 LootIdSet ids_set;
1239 LootTemplates_Reference.LoadAndCollectLootIds(ids_set);
1241 // check references and remove used
1242 LootTemplates_Creature.CheckLootRefs(&ids_set);
1243 LootTemplates_Fishing.CheckLootRefs(&ids_set);
1244 LootTemplates_Gameobject.CheckLootRefs(&ids_set);
1245 LootTemplates_Item.CheckLootRefs(&ids_set);
1246 LootTemplates_Milling.CheckLootRefs(&ids_set);
1247 LootTemplates_Pickpocketing.CheckLootRefs(&ids_set);
1248 LootTemplates_Skinning.CheckLootRefs(&ids_set);
1249 LootTemplates_Disenchant.CheckLootRefs(&ids_set);
1250 LootTemplates_Prospecting.CheckLootRefs(&ids_set);
1251 LootTemplates_QuestMail.CheckLootRefs(&ids_set);
1252 LootTemplates_Reference.CheckLootRefs(&ids_set);
1254 // output error for any still listed ids (not referenced from any loot table)
1255 LootTemplates_Reference.ReportUnusedIds(ids_set);