2 * Copyright (C) 2005-2010 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"
28 static eConfigFLoatValues
const qualityToRate
[MAX_ITEM_QUALITY
] = {
29 CONFIG_FLOAT_RATE_DROP_ITEM_POOR
, // ITEM_QUALITY_POOR
30 CONFIG_FLOAT_RATE_DROP_ITEM_NORMAL
, // ITEM_QUALITY_NORMAL
31 CONFIG_FLOAT_RATE_DROP_ITEM_UNCOMMON
, // ITEM_QUALITY_UNCOMMON
32 CONFIG_FLOAT_RATE_DROP_ITEM_RARE
, // ITEM_QUALITY_RARE
33 CONFIG_FLOAT_RATE_DROP_ITEM_EPIC
, // ITEM_QUALITY_EPIC
34 CONFIG_FLOAT_RATE_DROP_ITEM_LEGENDARY
, // ITEM_QUALITY_LEGENDARY
35 CONFIG_FLOAT_RATE_DROP_ITEM_ARTIFACT
, // ITEM_QUALITY_ARTIFACT
38 LootStore
LootTemplates_Creature( "creature_loot_template", "creature entry", true);
39 LootStore
LootTemplates_Disenchant( "disenchant_loot_template", "item disenchant id", true);
40 LootStore
LootTemplates_Fishing( "fishing_loot_template", "area id", true);
41 LootStore
LootTemplates_Gameobject( "gameobject_loot_template", "gameobject entry", true);
42 LootStore
LootTemplates_Item( "item_loot_template", "item entry", true);
43 LootStore
LootTemplates_Mail( "mail_loot_template", "mail template id", false);
44 LootStore
LootTemplates_Milling( "milling_loot_template", "item entry (herb)", true);
45 LootStore
LootTemplates_Pickpocketing("pickpocketing_loot_template","creature pickpocket lootid", true);
46 LootStore
LootTemplates_Prospecting( "prospecting_loot_template", "item entry (ore)", true);
47 LootStore
LootTemplates_Reference( "reference_loot_template", "reference id", false);
48 LootStore
LootTemplates_Skinning( "skinning_loot_template", "creature skinning id", true);
49 LootStore
LootTemplates_Spell( "spell_loot_template", "spell id (random item creating)",false);
51 class LootTemplate::LootGroup
// A set of loot definitions for items (refs are not allowed)
54 void AddEntry(LootStoreItem
& item
); // Adds an entry to the group (at loading stage)
55 bool HasQuestDrop() const; // True if group includes at least 1 quest drop entry
56 bool HasQuestDropForPlayer(Player
const * player
) const;
57 // The same for active quests of the player
58 void Process(Loot
& loot
) const; // Rolls an item from the group (if any) and adds the item to the loot
59 float RawTotalChance() const; // Overall chance for the group (without equal chanced items)
60 float TotalChance() const; // Overall chance for the group
62 void Verify(LootStore
const& lootstore
, uint32 id
, uint32 group_id
) const;
63 void CollectLootIds(LootIdSet
& set
) const;
64 void CheckLootRefs(LootIdSet
* ref_set
) const;
66 LootStoreItemList ExplicitlyChanced
; // Entries with chances defined in DB
67 LootStoreItemList EqualChanced
; // Zero chances - every entry takes the same chance
69 LootStoreItem
const * Roll() const; // Rolls an item from the group, returns NULL if all miss their chances
72 //Remove all data and free all memory
73 void LootStore::Clear()
75 for (LootTemplateMap::const_iterator itr
=m_LootTemplates
.begin(); itr
!= m_LootTemplates
.end(); ++itr
)
77 m_LootTemplates
.clear();
80 // Checks validity of the loot store
81 // Actual checks are done within LootTemplate::Verify() which is called for every template
82 void LootStore::Verify() const
84 for (LootTemplateMap::const_iterator i
= m_LootTemplates
.begin(); i
!= m_LootTemplates
.end(); ++i
)
85 i
->second
->Verify(*this, i
->first
);
88 // Loads a *_loot_template DB table into loot store
89 // All checks of the loaded template are called from here, no error reports at loot generation required
90 void LootStore::LoadLootTable()
92 LootTemplateMap::const_iterator tab
;
95 // Clearing store (for reloading case)
98 sLog
.outString( "%s :", GetName());
101 QueryResult
*result
= WorldDatabase
.PQuery("SELECT entry, item, ChanceOrQuestChance, groupid, mincountOrRef, maxcount, lootcondition, condition_value1, condition_value2 FROM %s",GetName());
105 barGoLink
bar((int)result
->GetRowCount());
109 Field
*fields
= result
->Fetch();
112 uint32 entry
= fields
[0].GetUInt32();
113 uint32 item
= fields
[1].GetUInt32();
114 float chanceOrQuestChance
= fields
[2].GetFloat();
115 uint8 group
= fields
[3].GetUInt8();
116 int32 mincountOrRef
= fields
[4].GetInt32();
117 uint32 maxcount
= fields
[5].GetUInt32();
118 ConditionType condition
= (ConditionType
)fields
[6].GetUInt8();
119 uint32 cond_value1
= fields
[7].GetUInt32();
120 uint32 cond_value2
= fields
[8].GetUInt32();
122 if(maxcount
> std::numeric_limits
<uint8
>::max())
124 sLog
.outErrorDb("Table '%s' entry %d item %d: maxcount value (%u) to large. must be less %u - skipped", GetName(), entry
, item
, maxcount
,std::numeric_limits
<uint8
>::max());
125 continue; // error already printed to log/console.
129 if(!PlayerCondition::IsValid(condition
,cond_value1
, cond_value2
))
131 sLog
.outErrorDb("... in table '%s' entry %u item %u", GetName(), entry
, item
);
132 continue; // error already printed to log/console.
135 // (condition + cond_value1/2) are converted into single conditionId
136 uint16 conditionId
= sObjectMgr
.GetConditionId(condition
, cond_value1
, cond_value2
);
138 LootStoreItem storeitem
= LootStoreItem(item
, chanceOrQuestChance
, group
, conditionId
, mincountOrRef
, maxcount
);
140 if (!storeitem
.IsValid(*this,entry
)) // Validity checks
143 // Looking for the template of the entry
144 // often entries are put together
145 if (m_LootTemplates
.empty() || tab
->first
!= entry
)
147 // Searching the template (in case template Id changed)
148 tab
= m_LootTemplates
.find(entry
);
149 if ( tab
== m_LootTemplates
.end() )
151 std::pair
< LootTemplateMap::iterator
, bool > pr
= m_LootTemplates
.insert(LootTemplateMap::value_type(entry
, new LootTemplate
));
155 // else is empty - template Id and iter are the same
156 // finally iter refers to already existed or just created <entry, LootTemplate>
158 // Adds current row to the template
159 tab
->second
->AddEntry(storeitem
);
162 } while (result
->NextRow());
166 Verify(); // Checks validity of the loot store
169 sLog
.outString( ">> Loaded %u loot definitions (%lu templates)", count
, (unsigned long)m_LootTemplates
.size());
174 sLog
.outErrorDb( ">> Loaded 0 loot definitions. DB table `%s` is empty.",GetName() );
178 bool LootStore::HaveQuestLootFor(uint32 loot_id
) const
180 LootTemplateMap::const_iterator itr
= m_LootTemplates
.find(loot_id
);
181 if(itr
== m_LootTemplates
.end())
184 // scan loot for quest items
185 return itr
->second
->HasQuestDrop(m_LootTemplates
);
188 bool LootStore::HaveQuestLootForPlayer(uint32 loot_id
,Player
* player
) const
190 LootTemplateMap::const_iterator tab
= m_LootTemplates
.find(loot_id
);
191 if (tab
!= m_LootTemplates
.end())
192 if (tab
->second
->HasQuestDropForPlayer(m_LootTemplates
, player
))
198 LootTemplate
const* LootStore::GetLootFor(uint32 loot_id
) const
200 LootTemplateMap::const_iterator tab
= m_LootTemplates
.find(loot_id
);
202 if (tab
== m_LootTemplates
.end())
208 void LootStore::LoadAndCollectLootIds(LootIdSet
& ids_set
)
212 for(LootTemplateMap::const_iterator tab
= m_LootTemplates
.begin(); tab
!= m_LootTemplates
.end(); ++tab
)
213 ids_set
.insert(tab
->first
);
216 void LootStore::CheckLootRefs(LootIdSet
* ref_set
) const
218 for(LootTemplateMap::const_iterator ltItr
= m_LootTemplates
.begin(); ltItr
!= m_LootTemplates
.end(); ++ltItr
)
219 ltItr
->second
->CheckLootRefs(ref_set
);
222 void LootStore::ReportUnusedIds(LootIdSet
const& ids_set
) const
224 // all still listed ids isn't referenced
225 for(LootIdSet::const_iterator itr
= ids_set
.begin(); itr
!= ids_set
.end(); ++itr
)
226 sLog
.outErrorDb("Table '%s' entry %d isn't %s and not referenced from loot, and then useless.", GetName(), *itr
,GetEntryName());
229 void LootStore::ReportNotExistedId(uint32 id
) const
231 sLog
.outErrorDb("Table '%s' entry %d (%s) not exist but used as loot id in DB.", GetName(), id
,GetEntryName());
235 // --------- LootStoreItem ---------
238 // Checks if the entry (quest, non-quest, reference) takes it's chance (at loot generation)
239 // RATE_DROP_ITEMS is no longer used for all types of entries
240 bool LootStoreItem::Roll(bool rate
) const
245 if(mincountOrRef
< 0) // reference case
246 return roll_chance_f(chance
* (rate
? sWorld
.getConfig(CONFIG_FLOAT_RATE_DROP_ITEM_REFERENCED
) : 1.0f
));
248 ItemPrototype
const *pProto
= ObjectMgr::GetItemPrototype(itemid
);
250 float qualityModifier
= pProto
&& rate
? sWorld
.getConfig(qualityToRate
[pProto
->Quality
]) : 1.0f
;
252 return roll_chance_f(chance
*qualityModifier
);
255 // Checks correctness of values
256 bool LootStoreItem::IsValid(LootStore
const& store
, uint32 entry
) const
258 if(group
>= 1 << 7) // it stored in 7 bit field
260 sLog
.outErrorDb("Table '%s' entry %d item %d: group (%u) must be less %u - skipped", store
.GetName(), entry
, itemid
, group
, 1 << 7);
264 if (mincountOrRef
== 0)
266 sLog
.outErrorDb("Table '%s' entry %d item %d: wrong mincountOrRef (%d) - skipped", store
.GetName(), entry
, itemid
, mincountOrRef
);
270 if( mincountOrRef
> 0 ) // item (quest or non-quest) entry, maybe grouped
272 ItemPrototype
const *proto
= ObjectMgr::GetItemPrototype(itemid
);
275 sLog
.outErrorDb("Table '%s' entry %d item %d: item entry not listed in `item_template` - skipped", store
.GetName(), entry
, itemid
);
279 if( chance
== 0 && group
== 0) // Zero chance is allowed for grouped entries only
281 sLog
.outErrorDb("Table '%s' entry %d item %d: equal-chanced grouped entry, but group not defined - skipped", store
.GetName(), entry
, itemid
);
285 if( chance
!= 0 && chance
< 0.000001f
) // loot with low chance
287 sLog
.outErrorDb("Table '%s' entry %d item %d: low chance (%f) - skipped",
288 store
.GetName(), entry
, itemid
, chance
);
292 if( maxcount
< mincountOrRef
) // wrong max count
294 sLog
.outErrorDb("Table '%s' entry %d item %d: max count (%u) less that min count (%i) - skipped", store
.GetName(), entry
, itemid
, uint32(maxcount
), mincountOrRef
);
299 else // mincountOrRef < 0
302 sLog
.outErrorDb("Table '%s' entry %d item %d: quest chance will be treated as non-quest chance", store
.GetName(), entry
, itemid
);
303 else if( chance
== 0 ) // no chance for the reference
305 sLog
.outErrorDb("Table '%s' entry %d item %d: zero chance is specified for a reference, skipped", store
.GetName(), entry
, itemid
);
309 return true; // Referenced template existence is checked at whole store level
313 // --------- LootItem ---------
316 // Constructor, copies most fields from LootStoreItem and generates random count
317 LootItem::LootItem(LootStoreItem
const& li
)
320 conditionId
= li
.conditionId
;
322 ItemPrototype
const* proto
= ObjectMgr::GetItemPrototype(itemid
);
323 freeforall
= proto
&& (proto
->Flags
& ITEM_FLAGS_PARTY_LOOT
);
325 needs_quest
= li
.needs_quest
;
327 count
= urand(li
.mincountOrRef
, li
.maxcount
); // constructor called for mincountOrRef > 0 only
328 randomSuffix
= GenerateEnchSuffixFactor(itemid
);
329 randomPropertyId
= Item::GenerateItemRandomPropertyId(itemid
);
332 is_underthreshold
= 0;
336 // Basic checks for player/item compatibility - if false no chance to see the item in the loot
337 bool LootItem::AllowedForPlayer(Player
const * player
) const
339 // DB conditions check
340 if ( !sObjectMgr
.IsPlayerMeetToCondition(player
,conditionId
) )
345 // Checking quests for quest-only drop (check only quests requirements in this case)
346 if( !player
->HasQuestForItem(itemid
) )
351 // Not quest only drop (check quest starting items for already accepted non-repeatable quests)
352 ItemPrototype
const *pProto
= ObjectMgr::GetItemPrototype(itemid
);
353 if (pProto
&& pProto
->StartQuest
&& player
->GetQuestStatus(pProto
->StartQuest
) != QUEST_STATUS_NONE
&& !player
->HasQuestForItem(itemid
))
361 // --------- Loot ---------
364 // Inserts the item into the loot (called by LootTemplate processors)
365 void Loot::AddItem(LootStoreItem
const & item
)
367 if (item
.needs_quest
) // Quest drop
369 if (quest_items
.size() < MAX_NR_QUEST_ITEMS
)
370 quest_items
.push_back(LootItem(item
));
372 else if (items
.size() < MAX_NR_LOOT_ITEMS
) // Non-quest drop
374 items
.push_back(LootItem(item
));
376 // non-conditional one-player only items are counted here,
377 // free for all items are counted in FillFFALoot(),
378 // non-ffa conditionals are counted in FillNonQuestNonFFAConditionalLoot()
379 if( !item
.conditionId
)
381 ItemPrototype
const* proto
= ObjectMgr::GetItemPrototype(item
.itemid
);
382 if( !proto
|| (proto
->Flags
& ITEM_FLAGS_PARTY_LOOT
)==0 )
388 // Calls processor of corresponding LootTemplate (which handles everything including references)
389 bool Loot::FillLoot(uint32 loot_id
, LootStore
const& store
, Player
* loot_owner
, bool personal
, bool noEmptyError
)
395 LootTemplate
const* tab
= store
.GetLootFor(loot_id
);
400 sLog
.outErrorDb("Table '%s' loot id #%u used but it doesn't have records.",store
.GetName(),loot_id
);
404 items
.reserve(MAX_NR_LOOT_ITEMS
);
405 quest_items
.reserve(MAX_NR_QUEST_ITEMS
);
407 tab
->Process(*this, store
,store
.IsRatesAllowed ()); // Processing is done there, callback via Loot::AddItem()
409 // Setting access rights for group loot case
410 Group
* pGroup
=loot_owner
->GetGroup();
411 if(!personal
&& pGroup
)
413 for(GroupReference
*itr
= pGroup
->GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
414 if(Player
* pl
= itr
->getSource())
415 FillNotNormalLootFor(pl
);
417 // ... for personal loot
419 FillNotNormalLootFor(loot_owner
);
424 void Loot::FillNotNormalLootFor(Player
* pl
)
426 uint32 plguid
= pl
->GetGUIDLow();
428 QuestItemMap::const_iterator qmapitr
= PlayerQuestItems
.find(plguid
);
429 if (qmapitr
== PlayerQuestItems
.end())
432 qmapitr
= PlayerFFAItems
.find(plguid
);
433 if (qmapitr
== PlayerFFAItems
.end())
436 qmapitr
= PlayerNonQuestNonFFAConditionalItems
.find(plguid
);
437 if (qmapitr
== PlayerNonQuestNonFFAConditionalItems
.end())
438 FillNonQuestNonFFAConditionalLoot(pl
);
441 QuestItemList
* Loot::FillFFALoot(Player
* player
)
443 QuestItemList
*ql
= new QuestItemList();
445 for(uint8 i
= 0; i
< items
.size(); ++i
)
447 LootItem
&item
= items
[i
];
448 if(!item
.is_looted
&& item
.freeforall
&& item
.AllowedForPlayer(player
) )
450 ql
->push_back(QuestItem(i
));
460 PlayerFFAItems
[player
->GetGUIDLow()] = ql
;
464 QuestItemList
* Loot::FillQuestLoot(Player
* player
)
466 if (items
.size() == MAX_NR_LOOT_ITEMS
) return NULL
;
467 QuestItemList
*ql
= new QuestItemList();
469 for(uint8 i
= 0; i
< quest_items
.size(); ++i
)
471 LootItem
&item
= quest_items
[i
];
472 if(!item
.is_looted
&& item
.AllowedForPlayer(player
) )
474 ql
->push_back(QuestItem(i
));
476 // questitems get blocked when they first apper in a
477 // player's quest vector
479 // increase once if one looter only, looter-times if free for all
480 if (item
.freeforall
|| !item
.is_blocked
)
483 item
.is_blocked
= true;
485 if (items
.size() + ql
->size() == MAX_NR_LOOT_ITEMS
)
495 PlayerQuestItems
[player
->GetGUIDLow()] = ql
;
499 QuestItemList
* Loot::FillNonQuestNonFFAConditionalLoot(Player
* player
)
501 QuestItemList
*ql
= new QuestItemList();
503 for(uint8 i
= 0; i
< items
.size(); ++i
)
505 LootItem
&item
= items
[i
];
506 if(!item
.is_looted
&& !item
.freeforall
&& item
.conditionId
&& item
.AllowedForPlayer(player
))
508 ql
->push_back(QuestItem(i
));
512 item
.is_counted
=true;
522 PlayerNonQuestNonFFAConditionalItems
[player
->GetGUIDLow()] = ql
;
526 //===================================================
528 void Loot::NotifyItemRemoved(uint8 lootIndex
)
530 // notify all players that are looting this that the item was removed
531 // convert the index to the slot the player sees
532 std::set
<uint64
>::iterator i_next
;
533 for(std::set
<uint64
>::iterator i
= PlayersLooting
.begin(); i
!= PlayersLooting
.end(); i
= i_next
)
537 if(Player
* pl
= ObjectAccessor::FindPlayer(*i
))
538 pl
->SendNotifyLootItemRemoved(lootIndex
);
540 PlayersLooting
.erase(i
);
544 void Loot::NotifyMoneyRemoved()
546 // notify all players that are looting this that the money was removed
547 std::set
<uint64
>::iterator i_next
;
548 for(std::set
<uint64
>::iterator i
= PlayersLooting
.begin(); i
!= PlayersLooting
.end(); i
= i_next
)
552 if(Player
* pl
= ObjectAccessor::FindPlayer(*i
))
553 pl
->SendNotifyLootMoneyRemoved();
555 PlayersLooting
.erase(i
);
559 void Loot::NotifyQuestItemRemoved(uint8 questIndex
)
561 // when a free for all questitem is looted
562 // all players will get notified of it being removed
563 // (other questitems can be looted by each group member)
564 // bit inefficient but isnt called often
566 std::set
<uint64
>::iterator i_next
;
567 for(std::set
<uint64
>::iterator i
= PlayersLooting
.begin(); i
!= PlayersLooting
.end(); i
= i_next
)
571 if(Player
* pl
= ObjectAccessor::FindPlayer(*i
))
573 QuestItemMap::const_iterator pq
= PlayerQuestItems
.find(pl
->GetGUIDLow());
574 if (pq
!= PlayerQuestItems
.end() && pq
->second
)
576 // find where/if the player has the given item in it's vector
577 QuestItemList
& pql
= *pq
->second
;
580 for (j
= 0; j
< pql
.size(); ++j
)
581 if (pql
[j
].index
== questIndex
)
585 pl
->SendNotifyLootItemRemoved(items
.size()+j
);
589 PlayersLooting
.erase(i
);
593 void Loot::generateMoneyLoot( uint32 minAmount
, uint32 maxAmount
)
597 if (maxAmount
<= minAmount
)
598 gold
= uint32(maxAmount
* sWorld
.getConfig(CONFIG_FLOAT_RATE_DROP_MONEY
));
599 else if ((maxAmount
- minAmount
) < 32700)
600 gold
= uint32(urand(minAmount
, maxAmount
) * sWorld
.getConfig(CONFIG_FLOAT_RATE_DROP_MONEY
));
602 gold
= uint32(urand(minAmount
>> 8, maxAmount
>> 8) * sWorld
.getConfig(CONFIG_FLOAT_RATE_DROP_MONEY
)) << 8;
606 LootItem
* Loot::LootItemInSlot(uint32 lootSlot
, Player
* player
, QuestItem
**qitem
, QuestItem
**ffaitem
, QuestItem
**conditem
)
608 LootItem
* item
= NULL
;
609 bool is_looted
= true;
610 if (lootSlot
>= items
.size())
612 uint32 questSlot
= lootSlot
- items
.size();
613 QuestItemMap::const_iterator itr
= PlayerQuestItems
.find(player
->GetGUIDLow());
614 if (itr
!= PlayerQuestItems
.end() && questSlot
< itr
->second
->size())
616 QuestItem
*qitem2
= &itr
->second
->at(questSlot
);
619 item
= &quest_items
[qitem2
->index
];
620 is_looted
= qitem2
->is_looted
;
625 item
= &items
[lootSlot
];
626 is_looted
= item
->is_looted
;
629 QuestItemMap::const_iterator itr
= PlayerFFAItems
.find(player
->GetGUIDLow());
630 if (itr
!= PlayerFFAItems
.end())
632 for(QuestItemList::const_iterator iter
=itr
->second
->begin(); iter
!= itr
->second
->end(); ++iter
)
633 if(iter
->index
==lootSlot
)
635 QuestItem
*ffaitem2
= (QuestItem
*)&(*iter
);
638 is_looted
= ffaitem2
->is_looted
;
643 else if(item
->conditionId
)
645 QuestItemMap::const_iterator itr
= PlayerNonQuestNonFFAConditionalItems
.find(player
->GetGUIDLow());
646 if (itr
!= PlayerNonQuestNonFFAConditionalItems
.end())
648 for(QuestItemList::const_iterator iter
=itr
->second
->begin(); iter
!= itr
->second
->end(); ++iter
)
650 if(iter
->index
==lootSlot
)
652 QuestItem
*conditem2
= (QuestItem
*)&(*iter
);
654 *conditem
= conditem2
;
655 is_looted
= conditem2
->is_looted
;
669 uint32
Loot::GetMaxSlotInLootFor(Player
* player
) const
671 QuestItemMap::const_iterator itr
= PlayerQuestItems
.find(player
->GetGUIDLow());
672 return items
.size() + (itr
!= PlayerQuestItems
.end() ? itr
->second
->size() : 0);
675 ByteBuffer
& operator<<(ByteBuffer
& b
, LootItem
const& li
)
677 b
<< uint32(li
.itemid
);
678 b
<< uint32(li
.count
); // nr of items of this type
679 b
<< uint32(ObjectMgr::GetItemPrototype(li
.itemid
)->DisplayInfoID
);
680 b
<< uint32(li
.randomSuffix
);
681 b
<< uint32(li
.randomPropertyId
);
682 //b << uint8(0); // slot type - will send after this function call
686 ByteBuffer
& operator<<(ByteBuffer
& b
, LootView
const& lv
)
688 if (lv
.permission
== NONE_PERMISSION
)
690 b
<< uint32(0); //gold
691 b
<< uint8(0); // item count
692 return b
; // nothing output more
697 uint8 itemsShown
= 0;
702 size_t count_pos
= b
.wpos(); // pos of item count byte
703 b
<< uint8(0); // item count placeholder
705 switch (lv
.permission
)
707 case GROUP_PERMISSION
:
709 // You are not the items proprietary, so you can only see
710 // blocked rolled items and quest items, and !ffa items
711 for (uint8 i
= 0; i
< l
.items
.size(); ++i
)
713 if (!l
.items
[i
].is_looted
&& !l
.items
[i
].freeforall
&& !l
.items
[i
].conditionId
&& l
.items
[i
].AllowedForPlayer(lv
.viewer
))
715 uint8 slot_type
= (l
.items
[i
].is_blocked
|| l
.items
[i
].is_underthreshold
) ? 0 : 1;
717 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
718 b
<< uint8(slot_type
); // 0 - get 1 - look only
725 case MASTER_PERMISSION
:
727 uint8 slot_type
= (lv
.permission
==MASTER_PERMISSION
) ? 2 : 0;
728 for (uint8 i
= 0; i
< l
.items
.size(); ++i
)
730 if (!l
.items
[i
].is_looted
&& !l
.items
[i
].freeforall
&& !l
.items
[i
].conditionId
&& l
.items
[i
].AllowedForPlayer(lv
.viewer
))
732 b
<< uint8(i
) << l
.items
[i
]; //only send one-player loot items now, free for all will be sent later
733 b
<< uint8(slot_type
); // 0 - get 2 - master selection
740 return b
; // nothing output more
743 QuestItemMap
const& lootPlayerQuestItems
= l
.GetPlayerQuestItems();
744 QuestItemMap::const_iterator q_itr
= lootPlayerQuestItems
.find(lv
.viewer
->GetGUIDLow());
745 if (q_itr
!= lootPlayerQuestItems
.end())
747 QuestItemList
*q_list
= q_itr
->second
;
748 for (QuestItemList::const_iterator qi
= q_list
->begin() ; qi
!= q_list
->end(); ++qi
)
750 LootItem
&item
= l
.quest_items
[qi
->index
];
751 if (!qi
->is_looted
&& !item
.is_looted
)
753 b
<< uint8(l
.items
.size() + (qi
- q_list
->begin()));
755 b
<< uint8(0); // allow loot
761 QuestItemMap
const& lootPlayerFFAItems
= l
.GetPlayerFFAItems();
762 QuestItemMap::const_iterator ffa_itr
= lootPlayerFFAItems
.find(lv
.viewer
->GetGUIDLow());
763 if (ffa_itr
!= lootPlayerFFAItems
.end())
765 QuestItemList
*ffa_list
= ffa_itr
->second
;
766 for (QuestItemList::const_iterator fi
= ffa_list
->begin() ; fi
!= ffa_list
->end(); ++fi
)
768 LootItem
&item
= l
.items
[fi
->index
];
769 if (!fi
->is_looted
&& !item
.is_looted
)
771 b
<< uint8(fi
->index
) << item
;
772 b
<< uint8(0); // allow loot
778 QuestItemMap
const& lootPlayerNonQuestNonFFAConditionalItems
= l
.GetPlayerNonQuestNonFFAConditionalItems();
779 QuestItemMap::const_iterator nn_itr
= lootPlayerNonQuestNonFFAConditionalItems
.find(lv
.viewer
->GetGUIDLow());
780 if (nn_itr
!= lootPlayerNonQuestNonFFAConditionalItems
.end())
782 QuestItemList
*conditional_list
= nn_itr
->second
;
783 for (QuestItemList::const_iterator ci
= conditional_list
->begin() ; ci
!= conditional_list
->end(); ++ci
)
785 LootItem
&item
= l
.items
[ci
->index
];
786 if (!ci
->is_looted
&& !item
.is_looted
)
788 b
<< uint8(ci
->index
) << item
;
789 b
<< uint8(0); // allow loot
795 //update number of items shown
796 b
.put
<uint8
>(count_pos
,itemsShown
);
802 // --------- LootTemplate::LootGroup ---------
805 // Adds an entry to the group (at loading stage)
806 void LootTemplate::LootGroup::AddEntry(LootStoreItem
& item
)
808 if (item
.chance
!= 0)
809 ExplicitlyChanced
.push_back(item
);
811 EqualChanced
.push_back(item
);
814 // Rolls an item from the group, returns NULL if all miss their chances
815 LootStoreItem
const * LootTemplate::LootGroup::Roll() const
817 if (!ExplicitlyChanced
.empty()) // First explicitly chanced entries are checked
819 float Roll
= rand_chance_f();
821 for (uint32 i
=0; i
<ExplicitlyChanced
.size(); ++i
) //check each explicitly chanced entry in the template and modify its chance based on quality.
823 if(ExplicitlyChanced
[i
].chance
>=100.0f
)
824 return &ExplicitlyChanced
[i
];
826 Roll
-= ExplicitlyChanced
[i
].chance
;
828 return &ExplicitlyChanced
[i
];
831 if (!EqualChanced
.empty()) // If nothing selected yet - an item is taken from equal-chanced part
832 return &EqualChanced
[irand(0, EqualChanced
.size()-1)];
834 return NULL
; // Empty drop from the group
837 // True if group includes at least 1 quest drop entry
838 bool LootTemplate::LootGroup::HasQuestDrop() const
840 for (LootStoreItemList::const_iterator i
=ExplicitlyChanced
.begin(); i
!= ExplicitlyChanced
.end(); ++i
)
843 for (LootStoreItemList::const_iterator i
=EqualChanced
.begin(); i
!= EqualChanced
.end(); ++i
)
849 // True if group includes at least 1 quest drop entry for active quests of the player
850 bool LootTemplate::LootGroup::HasQuestDropForPlayer(Player
const * player
) const
852 for (LootStoreItemList::const_iterator i
=ExplicitlyChanced
.begin(); i
!= ExplicitlyChanced
.end(); ++i
)
853 if (player
->HasQuestForItem(i
->itemid
))
855 for (LootStoreItemList::const_iterator i
=EqualChanced
.begin(); i
!= EqualChanced
.end(); ++i
)
856 if (player
->HasQuestForItem(i
->itemid
))
861 // Rolls an item from the group (if any takes its chance) and adds the item to the loot
862 void LootTemplate::LootGroup::Process(Loot
& loot
) const
864 LootStoreItem
const * item
= Roll();
869 // Overall chance for the group without equal chanced items
870 float LootTemplate::LootGroup::RawTotalChance() const
874 for (LootStoreItemList::const_iterator i
=ExplicitlyChanced
.begin(); i
!= ExplicitlyChanced
.end(); ++i
)
875 if ( !i
->needs_quest
)
881 // Overall chance for the group
882 float LootTemplate::LootGroup::TotalChance() const
884 float result
= RawTotalChance();
886 if (!EqualChanced
.empty() && result
< 100.0f
)
892 void LootTemplate::LootGroup::Verify(LootStore
const& lootstore
, uint32 id
, uint32 group_id
) const
894 float chance
= RawTotalChance();
895 if (chance
> 101.0f
) // TODO: replace with 100% when DBs will be ready
897 sLog
.outErrorDb("Table '%s' entry %u group %d has total chance > 100%% (%f)", lootstore
.GetName(), id
, group_id
, chance
);
900 if(chance
>= 100.0f
&& !EqualChanced
.empty())
902 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
);
906 void LootTemplate::LootGroup::CheckLootRefs(LootIdSet
* ref_set
) const
908 for (LootStoreItemList::const_iterator ieItr
=ExplicitlyChanced
.begin(); ieItr
!= ExplicitlyChanced
.end(); ++ieItr
)
910 if(ieItr
->mincountOrRef
< 0)
912 if(!LootTemplates_Reference
.GetLootFor(-ieItr
->mincountOrRef
))
913 LootTemplates_Reference
.ReportNotExistedId(-ieItr
->mincountOrRef
);
915 ref_set
->erase(-ieItr
->mincountOrRef
);
919 for (LootStoreItemList::const_iterator ieItr
=EqualChanced
.begin(); ieItr
!= EqualChanced
.end(); ++ieItr
)
921 if(ieItr
->mincountOrRef
< 0)
923 if(!LootTemplates_Reference
.GetLootFor(-ieItr
->mincountOrRef
))
924 LootTemplates_Reference
.ReportNotExistedId(-ieItr
->mincountOrRef
);
926 ref_set
->erase(-ieItr
->mincountOrRef
);
932 // --------- LootTemplate ---------
935 // Adds an entry to the group (at loading stage)
936 void LootTemplate::AddEntry(LootStoreItem
& item
)
938 if (item
.group
> 0 && item
.mincountOrRef
> 0) // Group
940 if (item
.group
>= Groups
.size())
941 Groups
.resize(item
.group
); // Adds new group the the loot template if needed
942 Groups
[item
.group
-1].AddEntry(item
); // Adds new entry to the group
944 else // Non-grouped entries and references are stored together
945 Entries
.push_back(item
);
948 // Rolls for every item in the template and adds the rolled items the the loot
949 void LootTemplate::Process(Loot
& loot
, LootStore
const& store
, bool rate
, uint8 groupId
) const
951 if (groupId
) // Group reference uses own processing of the group
953 if (groupId
> Groups
.size())
954 return; // Error message already printed at loading stage
956 Groups
[groupId
-1].Process(loot
);
960 // Rolling non-grouped items
961 for (LootStoreItemList::const_iterator i
= Entries
.begin() ; i
!= Entries
.end() ; ++i
)
964 continue; // Bad luck for the entry
966 if (i
->mincountOrRef
< 0) // References processing
968 LootTemplate
const* Referenced
= LootTemplates_Reference
.GetLootFor(-i
->mincountOrRef
);
971 continue; // Error message already printed at loading stage
973 for (uint32 loop
=0; loop
< i
->maxcount
; ++loop
)// Ref multiplicator
974 Referenced
->Process(loot
, store
, rate
, i
->group
);
976 else // Plain entries (not a reference, not grouped)
977 loot
.AddItem(*i
); // Chance is already checked, just add
980 // Now processing groups
981 for (LootGroups::const_iterator i
= Groups
.begin( ) ; i
!= Groups
.end( ) ; ++i
)
985 // True if template includes at least 1 quest drop entry
986 bool LootTemplate::HasQuestDrop(LootTemplateMap
const& store
, uint8 groupId
) const
988 if (groupId
) // Group reference
990 if (groupId
> Groups
.size())
991 return false; // Error message [should be] already printed at loading stage
992 return Groups
[groupId
-1].HasQuestDrop();
995 for (LootStoreItemList::const_iterator i
= Entries
.begin(); i
!= Entries
.end(); ++i
)
997 if (i
->mincountOrRef
< 0) // References
999 LootTemplateMap::const_iterator Referenced
= store
.find(-i
->mincountOrRef
);
1000 if( Referenced
==store
.end() )
1001 continue; // Error message [should be] already printed at loading stage
1002 if (Referenced
->second
->HasQuestDrop(store
, i
->group
) )
1005 else if ( i
->needs_quest
)
1006 return true; // quest drop found
1009 // Now processing groups
1010 for (LootGroups::const_iterator i
= Groups
.begin() ; i
!= Groups
.end() ; ++i
)
1011 if (i
->HasQuestDrop())
1017 // True if template includes at least 1 quest drop for an active quest of the player
1018 bool LootTemplate::HasQuestDropForPlayer(LootTemplateMap
const& store
, Player
const* player
, uint8 groupId
) const
1020 if (groupId
) // Group reference
1022 if (groupId
> Groups
.size())
1023 return false; // Error message already printed at loading stage
1024 return Groups
[groupId
-1].HasQuestDropForPlayer(player
);
1027 // Checking non-grouped entries
1028 for (LootStoreItemList::const_iterator i
= Entries
.begin() ; i
!= Entries
.end() ; ++i
)
1030 if (i
->mincountOrRef
< 0) // References processing
1032 LootTemplateMap::const_iterator Referenced
= store
.find(-i
->mincountOrRef
);
1033 if (Referenced
== store
.end() )
1034 continue; // Error message already printed at loading stage
1035 if (Referenced
->second
->HasQuestDropForPlayer(store
, player
, i
->group
) )
1038 else if ( player
->HasQuestForItem(i
->itemid
) )
1039 return true; // active quest drop found
1042 // Now checking groups
1043 for (LootGroups::const_iterator i
= Groups
.begin(); i
!= Groups
.end(); ++i
)
1044 if (i
->HasQuestDropForPlayer(player
))
1050 // Checks integrity of the template
1051 void LootTemplate::Verify(LootStore
const& lootstore
, uint32 id
) const
1053 // Checking group chances
1054 for (uint32 i
=0; i
< Groups
.size(); ++i
)
1055 Groups
[i
].Verify(lootstore
,id
,i
+1);
1057 // TODO: References validity checks
1060 void LootTemplate::CheckLootRefs(LootIdSet
* ref_set
) const
1062 for(LootStoreItemList::const_iterator ieItr
= Entries
.begin(); ieItr
!= Entries
.end(); ++ieItr
)
1064 if(ieItr
->mincountOrRef
< 0)
1066 if(!LootTemplates_Reference
.GetLootFor(-ieItr
->mincountOrRef
))
1067 LootTemplates_Reference
.ReportNotExistedId(-ieItr
->mincountOrRef
);
1069 ref_set
->erase(-ieItr
->mincountOrRef
);
1073 for(LootGroups::const_iterator grItr
= Groups
.begin(); grItr
!= Groups
.end(); ++grItr
)
1074 grItr
->CheckLootRefs(ref_set
);
1077 void LoadLootTemplates_Creature()
1079 LootIdSet ids_set
, ids_setUsed
;
1080 LootTemplates_Creature
.LoadAndCollectLootIds(ids_set
);
1082 // remove real entries and check existence loot
1083 for(uint32 i
= 1; i
< sCreatureStorage
.MaxEntry
; ++i
)
1085 if(CreatureInfo
const* cInfo
= sCreatureStorage
.LookupEntry
<CreatureInfo
>(i
))
1087 if(uint32 lootid
= cInfo
->lootid
)
1089 if (ids_set
.find(lootid
) == ids_set
.end())
1090 LootTemplates_Creature
.ReportNotExistedId(lootid
);
1092 ids_setUsed
.insert(lootid
);
1096 for(LootIdSet::const_iterator itr
= ids_setUsed
.begin(); itr
!= ids_setUsed
.end(); ++itr
)
1097 ids_set
.erase(*itr
);
1099 // for alterac valley we've defined Player-loot inside creature_loot_template id=0
1100 // this hack is used, so that we won't need to create an extra table player_loot_template for just one case
1103 // output error for any still listed (not referenced from appropriate table) ids
1104 LootTemplates_Creature
.ReportUnusedIds(ids_set
);
1107 void LoadLootTemplates_Disenchant()
1109 LootIdSet ids_set
, ids_setUsed
;
1110 LootTemplates_Disenchant
.LoadAndCollectLootIds(ids_set
);
1112 // remove real entries and check existence loot
1113 for(uint32 i
= 1; i
< sItemStorage
.MaxEntry
; ++i
)
1115 if(ItemPrototype
const* proto
= sItemStorage
.LookupEntry
<ItemPrototype
>(i
))
1117 if(uint32 lootid
= proto
->DisenchantID
)
1119 if (ids_set
.find(lootid
) == ids_set
.end())
1120 LootTemplates_Disenchant
.ReportNotExistedId(lootid
);
1122 ids_setUsed
.insert(lootid
);
1126 for(LootIdSet::const_iterator itr
= ids_setUsed
.begin(); itr
!= ids_setUsed
.end(); ++itr
)
1127 ids_set
.erase(*itr
);
1128 // output error for any still listed (not referenced from appropriate table) ids
1129 LootTemplates_Disenchant
.ReportUnusedIds(ids_set
);
1132 void LoadLootTemplates_Fishing()
1135 LootTemplates_Fishing
.LoadAndCollectLootIds(ids_set
);
1137 // remove real entries and check existence loot
1138 for(uint32 i
= 1; i
< sAreaStore
.GetNumRows(); ++i
)
1140 if(AreaTableEntry
const* areaEntry
= sAreaStore
.LookupEntry(i
))
1141 if (ids_set
.find(areaEntry
->ID
) != ids_set
.end())
1142 ids_set
.erase(areaEntry
->ID
);
1145 // output error for any still listed (not referenced from appropriate table) ids
1146 LootTemplates_Fishing
.ReportUnusedIds(ids_set
);
1149 void LoadLootTemplates_Gameobject()
1151 LootIdSet ids_set
, ids_setUsed
;
1152 LootTemplates_Gameobject
.LoadAndCollectLootIds(ids_set
);
1154 // remove real entries and check existence loot
1155 for(uint32 i
= 1; i
< sGOStorage
.MaxEntry
; ++i
)
1157 if(GameObjectInfo
const* gInfo
= sGOStorage
.LookupEntry
<GameObjectInfo
>(i
))
1159 if(uint32 lootid
= gInfo
->GetLootId())
1161 if (ids_set
.find(lootid
) == ids_set
.end())
1162 LootTemplates_Gameobject
.ReportNotExistedId(lootid
);
1164 ids_setUsed
.insert(lootid
);
1168 for(LootIdSet::const_iterator itr
= ids_setUsed
.begin(); itr
!= ids_setUsed
.end(); ++itr
)
1169 ids_set
.erase(*itr
);
1171 // output error for any still listed (not referenced from appropriate table) ids
1172 LootTemplates_Gameobject
.ReportUnusedIds(ids_set
);
1175 void LoadLootTemplates_Item()
1178 LootTemplates_Item
.LoadAndCollectLootIds(ids_set
);
1180 // remove real entries and check existence loot
1181 for(uint32 i
= 1; i
< sItemStorage
.MaxEntry
; ++i
)
1182 if(ItemPrototype
const* proto
= sItemStorage
.LookupEntry
<ItemPrototype
>(i
))
1183 if (ids_set
.find(proto
->ItemId
) != ids_set
.end())
1184 ids_set
.erase(proto
->ItemId
);
1186 // output error for any still listed (not referenced from appropriate table) ids
1187 LootTemplates_Item
.ReportUnusedIds(ids_set
);
1190 void LoadLootTemplates_Milling()
1193 LootTemplates_Milling
.LoadAndCollectLootIds(ids_set
);
1195 // remove real entries and check existence loot
1196 for(uint32 i
= 1; i
< sItemStorage
.MaxEntry
; ++i
)
1198 ItemPrototype
const* proto
= sItemStorage
.LookupEntry
<ItemPrototype
>(i
);
1202 if((proto
->BagFamily
& BAG_FAMILY_MASK_HERBS
)==0)
1205 if (ids_set
.find(proto
->ItemId
) != ids_set
.end())
1206 ids_set
.erase(proto
->ItemId
);
1209 // output error for any still listed (not referenced from appropriate table) ids
1210 LootTemplates_Milling
.ReportUnusedIds(ids_set
);
1213 void LoadLootTemplates_Pickpocketing()
1215 LootIdSet ids_set
, ids_setUsed
;
1216 LootTemplates_Pickpocketing
.LoadAndCollectLootIds(ids_set
);
1218 // remove real entries and check existence loot
1219 for(uint32 i
= 1; i
< sCreatureStorage
.MaxEntry
; ++i
)
1221 if(CreatureInfo
const* cInfo
= sCreatureStorage
.LookupEntry
<CreatureInfo
>(i
))
1223 if(uint32 lootid
= cInfo
->pickpocketLootId
)
1225 if (ids_set
.find(lootid
) == ids_set
.end())
1226 LootTemplates_Pickpocketing
.ReportNotExistedId(lootid
);
1228 ids_setUsed
.insert(lootid
);
1232 for(LootIdSet::const_iterator itr
= ids_setUsed
.begin(); itr
!= ids_setUsed
.end(); ++itr
)
1233 ids_set
.erase(*itr
);
1235 // output error for any still listed (not referenced from appropriate table) ids
1236 LootTemplates_Pickpocketing
.ReportUnusedIds(ids_set
);
1239 void LoadLootTemplates_Prospecting()
1242 LootTemplates_Prospecting
.LoadAndCollectLootIds(ids_set
);
1244 // remove real entries and check existence loot
1245 for(uint32 i
= 1; i
< sItemStorage
.MaxEntry
; ++i
)
1247 ItemPrototype
const* proto
= sItemStorage
.LookupEntry
<ItemPrototype
>(i
);
1251 if((proto
->BagFamily
& BAG_FAMILY_MASK_MINING_SUPP
)==0)
1254 if (ids_set
.find(proto
->ItemId
) != ids_set
.end())
1255 ids_set
.erase(proto
->ItemId
);
1258 // output error for any still listed (not referenced from appropriate table) ids
1259 LootTemplates_Prospecting
.ReportUnusedIds(ids_set
);
1262 void LoadLootTemplates_Mail()
1265 LootTemplates_Mail
.LoadAndCollectLootIds(ids_set
);
1267 // remove real entries and check existence loot
1268 for(uint32 i
= 1; i
< sMailTemplateStore
.GetNumRows(); ++i
)
1269 if(sMailTemplateStore
.LookupEntry(i
))
1270 if (ids_set
.find(i
) != ids_set
.end())
1273 // output error for any still listed (not referenced from appropriate table) ids
1274 LootTemplates_Mail
.ReportUnusedIds(ids_set
);
1277 void LoadLootTemplates_Skinning()
1279 LootIdSet ids_set
, ids_setUsed
;
1280 LootTemplates_Skinning
.LoadAndCollectLootIds(ids_set
);
1282 // remove real entries and check existence loot
1283 for(uint32 i
= 1; i
< sCreatureStorage
.MaxEntry
; ++i
)
1285 if(CreatureInfo
const* cInfo
= sCreatureStorage
.LookupEntry
<CreatureInfo
>(i
))
1287 if(uint32 lootid
= cInfo
->SkinLootId
)
1289 if (ids_set
.find(lootid
) == ids_set
.end())
1290 LootTemplates_Skinning
.ReportNotExistedId(lootid
);
1292 ids_setUsed
.insert(lootid
);
1296 for(LootIdSet::const_iterator itr
= ids_setUsed
.begin(); itr
!= ids_setUsed
.end(); ++itr
)
1297 ids_set
.erase(*itr
);
1299 // output error for any still listed (not referenced from appropriate table) ids
1300 LootTemplates_Skinning
.ReportUnusedIds(ids_set
);
1303 void LoadLootTemplates_Spell()
1306 LootTemplates_Spell
.LoadAndCollectLootIds(ids_set
);
1308 // remove real entries and check existence loot
1309 for(uint32 spell_id
= 1; spell_id
< sSpellStore
.GetNumRows(); ++spell_id
)
1311 SpellEntry
const* spellInfo
= sSpellStore
.LookupEntry (spell_id
);
1316 if( !IsLootCraftingSpell(spellInfo
))
1319 if (ids_set
.find(spell_id
) == ids_set
.end())
1321 // not report about not trainable spells (optionally supported by DB)
1322 // ignore 61756 (Northrend Inscription Research (FAST QA VERSION) for example
1323 if (!(spellInfo
->Attributes
& SPELL_ATTR_NOT_SHAPESHIFT
) || (spellInfo
->Attributes
& SPELL_ATTR_TRADESPELL
))
1325 LootTemplates_Spell
.ReportNotExistedId(spell_id
);
1329 ids_set
.erase(spell_id
);
1332 // output error for any still listed (not referenced from appropriate table) ids
1333 LootTemplates_Spell
.ReportUnusedIds(ids_set
);
1336 void LoadLootTemplates_Reference()
1339 LootTemplates_Reference
.LoadAndCollectLootIds(ids_set
);
1341 // check references and remove used
1342 LootTemplates_Creature
.CheckLootRefs(&ids_set
);
1343 LootTemplates_Fishing
.CheckLootRefs(&ids_set
);
1344 LootTemplates_Gameobject
.CheckLootRefs(&ids_set
);
1345 LootTemplates_Item
.CheckLootRefs(&ids_set
);
1346 LootTemplates_Milling
.CheckLootRefs(&ids_set
);
1347 LootTemplates_Pickpocketing
.CheckLootRefs(&ids_set
);
1348 LootTemplates_Skinning
.CheckLootRefs(&ids_set
);
1349 LootTemplates_Disenchant
.CheckLootRefs(&ids_set
);
1350 LootTemplates_Prospecting
.CheckLootRefs(&ids_set
);
1351 LootTemplates_Mail
.CheckLootRefs(&ids_set
);
1352 LootTemplates_Reference
.CheckLootRefs(&ids_set
);
1354 // output error for any still listed ids (not referenced from any loot table)
1355 LootTemplates_Reference
.ReportUnusedIds(ids_set
);