root/trunk/src/game/LootMgr.cpp @ 22

Revision 2, 44.5 kB (checked in by yumileroy, 17 years ago)

[svn] * Proper SVN structure

Original author: Neo2003
Date: 2008-10-02 16:23:55-05:00

Line 
1/*
2 * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
3 *
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.
8 *
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.
13 *
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
17 */
18
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
27static 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
35};
36
37LootStore LootTemplates_Creature(     "creature_loot_template",     "creature entry");
38LootStore LootTemplates_Disenchant(   "disenchant_loot_template",   "item disenchant id");
39LootStore LootTemplates_Fishing(      "fishing_loot_template",      "area id");
40LootStore LootTemplates_Gameobject(   "gameobject_loot_template",   "gameobject entry");
41LootStore LootTemplates_Item(         "item_loot_template",         "item entry");
42LootStore LootTemplates_Pickpocketing("pickpocketing_loot_template","creature pickpocket lootid");
43LootStore LootTemplates_Prospecting(  "prospecting_loot_template",  "item entry");
44LootStore LootTemplates_QuestMail(    "quest_mail_loot_template",   "quest id");
45LootStore LootTemplates_Reference(    "reference_loot_template",    "reference id");
46LootStore LootTemplates_Skinning(     "skinning_loot_template",     "creature skinning id");
47
48
49class LootTemplate::LootGroup                               // A set of loot definitions for items (refs are not allowed)
50{
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
59
60        void Verify(LootStore const& lootstore, uint32 id, uint32 group_id) const;
61        void CollectLootIds(LootIdSet& set) const;
62        void CheckLootRefs(LootTemplateMap const& store, 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
66
67        LootStoreItem const * Roll() const;                 // Rolls an item from the group, returns NULL if all miss their chances
68};
69
70//Remove all data and free all memory
71void LootStore::Clear()
72{
73    for (LootTemplateMap::const_iterator itr=m_LootTemplates.begin(); itr != m_LootTemplates.end(); ++itr)
74        delete itr->second;
75    m_LootTemplates.clear();
76}
77
78// Checks validity of the loot store
79// Actual checks are done within LootTemplate::Verify() which is called for every template
80void LootStore::Verify() const
81{
82    for (LootTemplateMap::const_iterator i = m_LootTemplates.begin(); i != m_LootTemplates.end(); ++i )
83        i->second->Verify(*this, i->first);
84}
85
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
88void LootStore::LoadLootTable()
89{
90    LootTemplateMap::iterator tab;
91    uint32 count = 0;
92
93    // Clearing store (for reloading case)
94    Clear();
95
96    sLog.outString( "%s :", GetName());
97
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());
100
101    if (result)
102    {
103        barGoLink bar(result->GetRowCount());
104
105        do
106        {
107            Field *fields = result->Fetch();
108            bar.step();
109
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();
119
120            if(!PlayerCondition::IsValid(condition,cond_value1, cond_value2))
121            {
122                sLog.outErrorDb("... in table '%s' entry %u item %u", GetName(), entry, item);
123                continue;                                   // error already printed to log/console.
124            }
125
126            // (condition + cond_value1/2) are converted into single conditionId
127            uint16 conditionId = objmgr.GetConditionId(condition, cond_value1, cond_value2);
128
129            LootStoreItem storeitem = LootStoreItem(item, chanceOrQuestChance, group, conditionId, mincountOrRef, maxcount);
130
131            if (!storeitem.IsValid(*this,entry))            // Validity checks
132                continue;
133
134            // Looking for the template of the entry
135                                                            // often entries are put together
136            if (m_LootTemplates.empty() || tab->first != entry)
137            {
138                // Searching the template (in case template Id changed)
139                tab = m_LootTemplates.find(entry);
140                if ( tab == m_LootTemplates.end() )
141                {
142                    std::pair< LootTemplateMap::iterator, bool > pr = m_LootTemplates.insert(LootTemplateMap::value_type(entry, new LootTemplate));
143                    tab = pr.first;
144                }
145            }
146            // else is empty - template Id and iter are the same
147            // finally iter refers to already existed or just created <entry, LootTemplate>
148
149            // Adds current row to the template
150            tab->second->AddEntry(storeitem);
151            ++count;
152
153        } while (result->NextRow());
154
155        delete result;
156
157        Verify();                                           // Checks validity of the loot store
158
159        sLog.outString();
160        sLog.outString( ">> Loaded %u loot definitions (%d templates)", count, m_LootTemplates.size());
161    }
162    else
163    {
164        sLog.outString();
165        sLog.outErrorDb( ">> Loaded 0 loot definitions. DB table `%s` is empty.",GetName() );
166    }
167}
168
169bool LootStore::HaveQuestLootFor(uint32 loot_id) const
170{
171    LootTemplateMap::const_iterator itr = m_LootTemplates.find(loot_id);
172    if(itr == m_LootTemplates.end())
173        return false;
174
175    // scan loot for quest items
176    return itr->second->HasQuestDrop(m_LootTemplates);
177}
178
179bool LootStore::HaveQuestLootForPlayer(uint32 loot_id,Player* player) const
180{
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;
185
186    return false;
187}
188
189LootTemplate const* LootStore::GetLootFor(uint32 loot_id) const
190{
191    LootTemplateMap::const_iterator tab = m_LootTemplates.find(loot_id);
192
193    if (tab == m_LootTemplates.end())
194        return NULL;
195
196    return tab->second;
197}
198
199void LootStore::LoadAndCollectLootIds(LootIdSet& ids_set)
200{
201    LoadLootTable();
202
203    for(LootTemplateMap::const_iterator tab = m_LootTemplates.begin(); tab != m_LootTemplates.end(); ++tab)
204        ids_set.insert(tab->first);
205}
206
207void LootStore::CheckLootRefs(LootIdSet* ref_set) const
208{
209    for(LootTemplateMap::const_iterator ltItr = m_LootTemplates.begin(); ltItr != m_LootTemplates.end(); ++ltItr)
210        ltItr->second->CheckLootRefs(m_LootTemplates,ref_set);
211}
212
213void LootStore::ReportUnusedIds(LootIdSet const& ids_set) const
214{
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());
218}
219
220void LootStore::ReportNotExistedId(uint32 id) const
221{
222    sLog.outErrorDb("Table '%s' entry %d (%s) not exist but used as loot id in DB.", GetName(), id,GetEntryName());
223}
224
225//
226// --------- LootStoreItem ---------
227//
228
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
231bool LootStoreItem::Roll() const
232{
233    if(chance>=100.f)
234        return true;
235
236    if(mincountOrRef < 0)                                   // reference case
237        return roll_chance_f(chance*sWorld.getRate(RATE_DROP_ITEM_REFERENCED));
238
239    ItemPrototype const *pProto = objmgr.GetItemPrototype(itemid);
240
241    float qualityModifier = pProto ? sWorld.getRate(qualityToRate[pProto->Quality]) : 1.0f;
242
243    return roll_chance_f(chance*qualityModifier);
244}
245
246// Checks correctness of values
247bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const
248{
249    if (mincountOrRef == 0)
250    {
251        sLog.outErrorDb("Table '%s' entry %d item %d: wrong mincountOrRef (%d) - skipped", store.GetName(), entry, itemid, mincountOrRef);
252        return false;
253    }
254
255    if( mincountOrRef > 0 )                                 // item (quest or non-quest) entry, maybe grouped
256    {
257        ItemPrototype const *proto = objmgr.GetItemPrototype(itemid);
258        if(!proto)
259        {
260            sLog.outErrorDb("Table '%s' entry %d item %d: item entry not listed in `item_template` - skipped", store.GetName(), entry, itemid);
261            return false;
262        }
263
264        if( chance == 0 && group == 0)                      // Zero chance is allowed for grouped entries only
265        {
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;
268        }
269
270        if( chance != 0 && chance < 0.000001f )             // loot with low chance
271        {
272            sLog.outErrorDb("Table '%s' entry %d item %d: low chance (%d) - skipped", store.GetName(), entry, itemid, chance);
273            return false;
274        }
275    }
276    else                                                    // mincountOrRef < 0
277    {
278        if (needs_quest)
279            sLog.outErrorDb("Table '%s' entry %d item %d: quest chance will be treated as non-quest chance", store.GetName(), entry, itemid);
280        else if( chance == 0 )                              // no chance for the reference
281        {
282            sLog.outErrorDb("Table '%s' entry %d item %d: zero chance is specified for a reference, skipped", store.GetName(), entry, itemid);
283            return false;
284        }
285    }
286    return true;                                            // Referenced template existence is checked at whole store level
287}
288
289//
290// --------- LootItem ---------
291//
292
293// Constructor, copies most fields from LootStoreItem and generates random count
294LootItem::LootItem(LootStoreItem const& li)
295{
296    itemid      = li.itemid;
297    conditionId = li.conditionId;
298
299    ItemPrototype const* proto = objmgr.GetItemPrototype(itemid);
300    freeforall  = proto && (proto->Flags & ITEM_FLAGS_PARTY_LOOT);
301
302    needs_quest = li.needs_quest;
303
304    count       = urand(li.mincountOrRef, li.maxcount);     // constructor called for mincountOrRef > 0 only
305    randomSuffix = GenerateEnchSuffixFactor(itemid);
306    randomPropertyId = Item::GenerateItemRandomPropertyId(itemid);
307    is_looted = 0;
308    is_blocked = 0;
309    is_underthreshold = 0;
310    is_counted = 0;
311}
312
313// Basic checks for player/item compatibility - if false no chance to see the item in the loot
314bool LootItem::AllowedForPlayer(Player const * player) const
315{
316    // DB conditions check
317    if ( !objmgr.IsPlayerMeetToCondition(player,conditionId) )
318        return false;
319
320    if ( needs_quest )
321    {
322        // Checking quests for quest-only drop (check only quests requirements in this case)
323        if( !player->HasQuestForItem(itemid) )
324            return false;
325    }
326    else
327    {
328        // Not quest only drop (check quest starting items for already accepted non-repeatable quests)
329        ItemPrototype const *pProto = objmgr.GetItemPrototype(itemid);
330        if (pProto && pProto->StartQuest && player->GetQuestStatus(pProto->StartQuest) != QUEST_STATUS_NONE && !player->HasQuestForItem(itemid))
331            return false;
332    }
333
334    return true;
335}
336
337//
338// --------- Loot ---------
339//
340
341// Inserts the item into the loot (called by LootTemplate processors)
342void Loot::AddItem(LootStoreItem const & item)
343{
344    if (item.needs_quest)                                   // Quest drop
345    {
346        if (quest_items.size() < MAX_NR_QUEST_ITEMS)
347            quest_items.push_back(LootItem(item));
348    }
349    else if (items.size() < MAX_NR_LOOT_ITEMS)              // Non-quest drop
350    {
351        items.push_back(LootItem(item));
352
353        // non-conditional one-player only items are counted here,
354        // free for all items are counted in FillFFALoot(),
355        // non-ffa conditionals are counted in FillNonQuestNonFFAConditionalLoot()
356        if( !item.conditionId )
357        {
358            ItemPrototype const* proto = objmgr.GetItemPrototype(item.itemid);
359            if( !proto || (proto->Flags & ITEM_FLAGS_PARTY_LOOT)==0 )
360                ++unlootedCount;
361        }
362    }
363}
364
365// Calls processor of corresponding LootTemplate (which handles everything including references)
366void Loot::FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner)
367{
368    LootTemplate const* tab = store.GetLootFor(loot_id);
369
370    if (!tab)
371    {
372        sLog.outErrorDb("Table '%s' loot id #%u used but it doesn't have records.",store.GetName(),loot_id);
373        return;
374    }
375
376    items.reserve(MAX_NR_LOOT_ITEMS);
377    quest_items.reserve(MAX_NR_QUEST_ITEMS);
378
379    tab->Process(*this, store);                             // Processing is done there, callback via Loot::AddItem()
380
381    // Setting access rights fow group-looting case
382    if(!loot_owner)
383        return;
384    Group * pGroup=loot_owner->GetGroup();
385    if(!pGroup)
386        return;
387    for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
388    {
389        //fill the quest item map for every player in the recipient's group
390        Player* pl = itr->getSource();
391        if(!pl)
392            continue;
393        uint32 plguid = pl->GetGUIDLow();
394        QuestItemMap::iterator qmapitr = PlayerQuestItems.find(plguid);
395        if (qmapitr == PlayerQuestItems.end())
396        {
397            FillQuestLoot(pl);
398        }
399        qmapitr = PlayerFFAItems.find(plguid);
400        if (qmapitr == PlayerFFAItems.end())
401        {
402            FillFFALoot(pl);
403        }
404        qmapitr = PlayerNonQuestNonFFAConditionalItems.find(plguid);
405        if (qmapitr == PlayerNonQuestNonFFAConditionalItems.end())
406        {
407            FillNonQuestNonFFAConditionalLoot(pl);
408        }
409    }
410}
411
412QuestItemList* Loot::FillFFALoot(Player* player)
413{
414    QuestItemList *ql = new QuestItemList();
415
416    for(uint8 i = 0; i < items.size(); i++)
417    {
418        LootItem &item = items[i];
419        if(!item.is_looted && item.freeforall && item.AllowedForPlayer(player) )
420        {
421            ql->push_back(QuestItem(i));
422            ++unlootedCount;
423        }
424    }
425    if (ql->empty())
426    {
427        delete ql;
428        return NULL;
429    }
430
431    PlayerFFAItems[player->GetGUIDLow()] = ql;
432    return ql;
433}
434
435QuestItemList* Loot::FillQuestLoot(Player* player)
436{
437    if (items.size() == MAX_NR_LOOT_ITEMS) return NULL;
438    QuestItemList *ql = new QuestItemList();
439
440    for(uint8 i = 0; i < quest_items.size(); i++)
441    {
442        LootItem &item = quest_items[i];
443        if(!item.is_looted && item.AllowedForPlayer(player) )
444        {
445            ql->push_back(QuestItem(i));
446
447            // questitems get blocked when they first apper in a
448            // player's quest vector
449            //
450            // increase once if one looter only, looter-times if free for all
451            if (item.freeforall || !item.is_blocked)
452                ++unlootedCount;
453
454            item.is_blocked = true;
455
456            if (items.size() + ql->size() == MAX_NR_LOOT_ITEMS)
457                break;
458        }
459    }
460    if (ql->empty())
461    {
462        delete ql;
463        return NULL;
464    }
465
466    PlayerQuestItems[player->GetGUIDLow()] = ql;
467    return ql;
468}
469
470QuestItemList* Loot::FillNonQuestNonFFAConditionalLoot(Player* player)
471{
472    QuestItemList *ql = new QuestItemList();
473
474    for(uint8 i = 0; i < items.size(); ++i)
475    {
476        LootItem &item = items[i];
477        if(!item.is_looted && !item.freeforall && item.conditionId && item.AllowedForPlayer(player))
478        {
479            ql->push_back(QuestItem(i));
480            if(!item.is_counted)
481            {
482                ++unlootedCount;
483                item.is_counted=true;
484            }
485        }
486    }
487    if (ql->empty())
488    {
489        delete ql;
490        return NULL;
491    }
492
493    PlayerNonQuestNonFFAConditionalItems[player->GetGUIDLow()] = ql;
494    return ql;
495}
496
497//===================================================
498
499void Loot::NotifyItemRemoved(uint8 lootIndex)
500{
501    // notify all players that are looting this that the item was removed
502    // convert the index to the slot the player sees
503    std::set<uint64>::iterator i_next;
504    for(std::set<uint64>::iterator i = PlayersLooting.begin(); i != PlayersLooting.end(); i = i_next)
505    {
506        i_next = i;
507        ++i_next;
508        if(Player* pl = ObjectAccessor::FindPlayer(*i))
509            pl->SendNotifyLootItemRemoved(lootIndex);
510        else
511            PlayersLooting.erase(i);
512    }
513}
514
515void Loot::NotifyMoneyRemoved()
516{
517    // notify all players that are looting this that the money was removed
518    std::set<uint64>::iterator i_next;
519    for(std::set<uint64>::iterator i = PlayersLooting.begin(); i != PlayersLooting.end(); i = i_next)
520    {
521        i_next = i;
522        ++i_next;
523        if(Player* pl = ObjectAccessor::FindPlayer(*i))
524            pl->SendNotifyLootMoneyRemoved();
525        else
526            PlayersLooting.erase(i);
527    }
528}
529
530void Loot::NotifyQuestItemRemoved(uint8 questIndex)
531{
532    // when a free for all questitem is looted
533    // all players will get notified of it being removed
534    // (other questitems can be looted by each group member)
535    // bit inefficient but isnt called often
536
537    std::set<uint64>::iterator i_next;
538    for(std::set<uint64>::iterator i = PlayersLooting.begin(); i != PlayersLooting.end(); i = i_next)
539    {
540        i_next = i;
541        ++i_next;
542        if(Player* pl = ObjectAccessor::FindPlayer(*i))
543        {
544            QuestItemMap::iterator pq = PlayerQuestItems.find(pl->GetGUIDLow());
545            if (pq != PlayerQuestItems.end() && pq->second)
546            {
547                // find where/if the player has the given item in it's vector
548                QuestItemList& pql = *pq->second;
549
550                uint8 j;
551                for (j = 0; j < pql.size(); ++j)
552                    if (pql[j].index == questIndex)
553                        break;
554
555                if (j < pql.size())
556                    pl->SendNotifyLootItemRemoved(items.size()+j);
557            }
558        }
559        else
560            PlayersLooting.erase(i);
561    }
562}
563
564void Loot::generateMoneyLoot( uint32 minAmount, uint32 maxAmount )
565{
566    if (maxAmount > 0)
567    {
568        if (maxAmount <= minAmount)
569            gold = uint32(maxAmount * sWorld.getRate(RATE_DROP_MONEY));
570        else if ((maxAmount - minAmount) < 32700)
571            gold = uint32(urand(minAmount, maxAmount) * sWorld.getRate(RATE_DROP_MONEY));
572        else
573            gold = uint32(urand(minAmount >> 8, maxAmount >> 8) * sWorld.getRate(RATE_DROP_MONEY)) << 8;
574    }
575}
576
577LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, QuestItem **qitem, QuestItem **ffaitem, QuestItem **conditem)
578{
579    LootItem* item = NULL;
580    bool is_looted = true;
581    if (lootSlot >= items.size())
582    {
583        uint32 questSlot = lootSlot - items.size();
584        QuestItemMap::const_iterator itr = PlayerQuestItems.find(player->GetGUIDLow());
585        if (itr != PlayerQuestItems.end() && questSlot < itr->second->size())
586        {
587            QuestItem *qitem2 = &itr->second->at(questSlot);
588            if(qitem)
589                *qitem = qitem2;
590            item = &quest_items[qitem2->index];
591            is_looted = qitem2->is_looted;
592        }
593    }
594    else
595    {
596        item = &items[lootSlot];
597        is_looted = item->is_looted;
598        if(item->freeforall)
599        {
600            QuestItemMap::const_iterator itr = PlayerFFAItems.find(player->GetGUIDLow());
601            if (itr != PlayerFFAItems.end())
602            {
603                for(QuestItemList::iterator iter=itr->second->begin(); iter!= itr->second->end(); ++iter)
604                    if(iter->index==lootSlot)
605                    {
606                        QuestItem *ffaitem2 = (QuestItem*)&(*iter);
607                        if(ffaitem)
608                            *ffaitem = ffaitem2;
609                        is_looted = ffaitem2->is_looted;
610                        break;
611                    }
612            }
613        }
614        else if(item->conditionId)
615        {
616            QuestItemMap::const_iterator itr = PlayerNonQuestNonFFAConditionalItems.find(player->GetGUIDLow());
617            if (itr != PlayerNonQuestNonFFAConditionalItems.end())
618            {
619                for(QuestItemList::iterator iter=itr->second->begin(); iter!= itr->second->end(); ++iter)
620                {
621                    if(iter->index==lootSlot)
622                    {
623                        QuestItem *conditem2 = (QuestItem*)&(*iter);
624                        if(conditem)
625                            *conditem = conditem2;
626                        is_looted = conditem2->is_looted;
627                        break;
628                    }
629                }
630            }
631        }
632    }
633
634    if(is_looted)
635        return NULL;
636
637    return item;
638}
639
640ByteBuffer& operator<<(ByteBuffer& b, LootItem const& li)
641{
642    b << uint32(li.itemid);
643    b << uint32(li.count);                                  // nr of items of this type
644    b << uint32(objmgr.GetItemPrototype(li.itemid)->DisplayInfoID);
645    b << uint32(li.randomSuffix);
646    b << uint32(li.randomPropertyId);
647    //b << uint8(0);                                        // slot type - will send after this function call
648    return b;
649}
650
651ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
652{
653    Loot &l = lv.loot;
654
655    uint8 itemsShown = 0;
656
657    //gold
658    b << uint32(lv.permission!=NONE_PERMISSION ? l.gold : 0);
659
660    size_t count_pos = b.wpos();                            // pos of item count byte
661    b << uint8(0);                                          // item count placeholder
662
663    switch (lv.permission)
664    {
665        case GROUP_PERMISSION:
666        {
667            // You are not the items proprietary, so you can only see
668            // blocked rolled items and quest items, and !ffa items
669            for (uint8 i = 0; i < l.items.size(); ++i)
670            {
671                if (!l.items[i].is_looted && !l.items[i].freeforall && !l.items[i].conditionId && l.items[i].AllowedForPlayer(lv.viewer))
672                {
673                    uint8 slot_type = (l.items[i].is_blocked || l.items[i].is_underthreshold) ? 0 : 1;
674
675                    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
676                    b << uint8(slot_type);                  // 0 - get 1 - look only
677                    ++itemsShown;
678                }
679            }
680            break;
681        }
682        case ALL_PERMISSION:
683        case MASTER_PERMISSION:
684        {
685            uint8 slot_type = (lv.permission==MASTER_PERMISSION) ? 2 : 0;
686            for (uint8 i = 0; i < l.items.size(); ++i)
687            {
688                if (!l.items[i].is_looted && !l.items[i].freeforall && !l.items[i].conditionId && l.items[i].AllowedForPlayer(lv.viewer))
689                {
690                    b << uint8(i) << l.items[i];            //only send one-player loot items now, free for all will be sent later
691                    b << uint8(slot_type);                  // 0 - get 2 - master selection
692                    ++itemsShown;
693                }
694            }
695            break;
696        }
697        case NONE_PERMISSION:
698        default:
699            return b;                                       // nothing output more
700    }
701
702    if (lv.qlist)
703    {
704        for (QuestItemList::iterator qi = lv.qlist->begin() ; qi != lv.qlist->end(); ++qi)
705        {
706            LootItem &item = l.quest_items[qi->index];
707            if (!qi->is_looted && !item.is_looted)
708            {
709                b << uint8(l.items.size() + (qi - lv.qlist->begin()));
710                b << item;
711                b << uint8(0);                              // allow loot
712                ++itemsShown;
713            }
714        }
715    }
716
717    if (lv.ffalist)
718    {
719        for (QuestItemList::iterator fi = lv.ffalist->begin() ; fi != lv.ffalist->end(); ++fi)
720        {
721            LootItem &item = l.items[fi->index];
722            if (!fi->is_looted && !item.is_looted)
723            {
724                b << uint8(fi->index) << item;
725                b << uint8(0);                              // allow loot
726                ++itemsShown;
727            }
728        }
729    }
730
731    if (lv.conditionallist)
732    {
733        for (QuestItemList::iterator ci = lv.conditionallist->begin() ; ci != lv.conditionallist->end(); ++ci)
734        {
735            LootItem &item = l.items[ci->index];
736            if (!ci->is_looted && !item.is_looted)
737            {
738                b << uint8(ci->index) << item;
739                b << uint8(0);                              // allow loot
740                ++itemsShown;
741            }
742        }
743    }
744
745    //update number of items shown
746    b.put<uint8>(count_pos,itemsShown);
747
748    return b;
749}
750
751//
752// --------- LootTemplate::LootGroup ---------
753//
754
755// Adds an entry to the group (at loading stage)
756void LootTemplate::LootGroup::AddEntry(LootStoreItem& item)
757{
758    if (item.chance != 0)
759        ExplicitlyChanced.push_back(item);
760    else
761        EqualChanced.push_back(item);
762}
763
764// Rolls an item from the group, returns NULL if all miss their chances
765LootStoreItem const * LootTemplate::LootGroup::Roll() const
766{
767    if (!ExplicitlyChanced.empty())                         // First explicitly chanced entries are checked
768    {
769        float Roll = rand_chance();
770
771        for (uint32 i=0; i<ExplicitlyChanced.size(); ++i)    //check each explicitly chanced entry in the template and modify its chance based on quality.
772        {
773            if(ExplicitlyChanced[i].chance>=100.f)
774                return &ExplicitlyChanced[i];
775
776            ItemPrototype const *pProto = objmgr.GetItemPrototype(ExplicitlyChanced[i].itemid);
777            float qualityMultiplier = pProto ? sWorld.getRate(qualityToRate[pProto->Quality]) : 1.0f;
778            Roll -= ExplicitlyChanced[i].chance * qualityMultiplier;
779            if (Roll < 0)
780                return &ExplicitlyChanced[i];
781        }
782    }
783    if (!EqualChanced.empty())                              // If nothing selected yet - an item is taken from equal-chanced part
784        return &EqualChanced[irand(0, EqualChanced.size()-1)];
785
786    return NULL;                                            // Empty drop from the group
787}
788
789// True if group includes at least 1 quest drop entry
790bool LootTemplate::LootGroup::HasQuestDrop() const
791{
792    for (LootStoreItemList::const_iterator i=ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i)
793        if (i->needs_quest)
794            return true;
795    for (LootStoreItemList::const_iterator i=EqualChanced.begin(); i != EqualChanced.end(); ++i)
796        if (i->needs_quest)
797            return true;
798    return false;
799}
800
801// True if group includes at least 1 quest drop entry for active quests of the player
802bool LootTemplate::LootGroup::HasQuestDropForPlayer(Player const * player) const
803{
804    for (LootStoreItemList::const_iterator i=ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i)
805        if (player->HasQuestForItem(i->itemid))
806            return true;
807    for (LootStoreItemList::const_iterator i=EqualChanced.begin(); i != EqualChanced.end(); ++i)
808        if (player->HasQuestForItem(i->itemid))
809            return true;
810    return false;
811}
812
813// Rolls an item from the group (if any takes its chance) and adds the item to the loot
814void LootTemplate::LootGroup::Process(Loot& loot) const
815{
816    LootStoreItem const * item = Roll();
817    if (item != NULL)
818        loot.AddItem(*item);
819}
820
821// Overall chance for the group without equal chanced items
822float LootTemplate::LootGroup::RawTotalChance() const
823{
824    float result = 0;
825
826    for (LootStoreItemList::const_iterator i=ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i)
827        if ( !i->needs_quest )
828            result += i->chance;
829
830    return result;
831}
832
833// Overall chance for the group
834float LootTemplate::LootGroup::TotalChance() const
835{
836    float result = RawTotalChance();
837
838    if (!EqualChanced.empty() && result < 100.0f)
839        return 100.0f;
840
841    return result;
842}
843
844void LootTemplate::LootGroup::Verify(LootStore const& lootstore, uint32 id, uint32 group_id) const
845{
846    float chance = RawTotalChance();
847    if (chance > 101.0f)                                    // TODO: replace with 100% when DBs will be ready
848    {
849        sLog.outErrorDb("Table '%s' entry %u group %d has total chance > 100%% (%f)", lootstore.GetName(), id, group_id, chance);
850    }
851
852    if(chance >= 100.0f && !EqualChanced.empty())
853    {
854        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);
855    }
856}
857
858void LootTemplate::LootGroup::CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const
859{
860    for (LootStoreItemList::const_iterator ieItr=ExplicitlyChanced.begin(); ieItr != ExplicitlyChanced.end(); ++ieItr)
861    {
862        if(ieItr->mincountOrRef < 0)
863        {
864            if(!LootTemplates_Reference.GetLootFor(-ieItr->mincountOrRef))
865                LootTemplates_Reference.ReportNotExistedId(-ieItr->mincountOrRef);
866            else if(ref_set)
867                ref_set->erase(-ieItr->mincountOrRef);
868        }
869    }
870
871    for (LootStoreItemList::const_iterator ieItr=EqualChanced.begin(); ieItr != EqualChanced.end(); ++ieItr)
872    {
873        if(ieItr->mincountOrRef < 0)
874        {
875            if(!LootTemplates_Reference.GetLootFor(-ieItr->mincountOrRef))
876                LootTemplates_Reference.ReportNotExistedId(-ieItr->mincountOrRef);
877            else if(ref_set)
878                ref_set->erase(-ieItr->mincountOrRef);
879        }
880    }
881}
882
883//
884// --------- LootTemplate ---------
885//
886
887// Adds an entry to the group (at loading stage)
888void LootTemplate::AddEntry(LootStoreItem& item)
889{
890    if (item.group > 0 && item.mincountOrRef > 0)           // Group
891    {
892        if (item.group >= Groups.size())
893            Groups.resize(item.group);                      // Adds new group the the loot template if needed
894        Groups[item.group-1].AddEntry(item);                // Adds new entry to the group
895    }
896    else                                                    // Non-grouped entries and references are stored together
897        Entries.push_back(item);
898}
899
900// Rolls for every item in the template and adds the rolled items the the loot
901void LootTemplate::Process(Loot& loot, LootStore const& store, uint8 groupId) const
902{
903    if (groupId)                                            // Group reference uses own processing of the group
904    {
905        if (groupId > Groups.size())
906            return;                                         // Error message already printed at loading stage
907
908        Groups[groupId-1].Process(loot);
909        return;
910    }
911
912    // Rolling non-grouped items
913    for (LootStoreItemList::const_iterator i = Entries.begin() ; i != Entries.end() ; i++ )
914    {
915        if ( !i->Roll() )
916            continue;                                       // Bad luck for the entry
917
918        if (i->mincountOrRef < 0)                           // References processing
919        {
920            LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(-i->mincountOrRef);
921
922            if(!Referenced)
923                continue;                                   // Error message already printed at loading stage
924
925            for (uint32 loop=0; loop < i->maxcount; ++loop )// Ref multiplicator
926                Referenced->Process(loot, store, i->group); // Ref processing
927        }
928        else                                                // Plain entries (not a reference, not grouped)
929            loot.AddItem(*i);                               // Chance is already checked, just add
930    }
931
932    // Now processing groups
933    for (LootGroups::const_iterator i = Groups.begin( ) ; i != Groups.end( ) ; i++ )
934        i->Process(loot);
935}
936
937// True if template includes at least 1 quest drop entry
938bool LootTemplate::HasQuestDrop(LootTemplateMap const& store, uint8 groupId) const
939{
940    if (groupId)                                            // Group reference
941    {
942        if (groupId > Groups.size())
943            return false;                                   // Error message [should be] already printed at loading stage
944        return Groups[groupId-1].HasQuestDrop();
945    }
946
947    for (LootStoreItemList::const_iterator i = Entries.begin(); i != Entries.end(); ++i )
948    {
949        if (i->mincountOrRef < 0)                           // References
950        {
951            LootTemplateMap::const_iterator Referenced = store.find(-i->mincountOrRef);
952            if( Referenced ==store.end() )
953                continue;                                   // Error message [should be] already printed at loading stage
954            if (Referenced->second->HasQuestDrop(store, i->group) )
955                return true;
956        }
957        else if ( i->needs_quest )
958            return true;                                    // quest drop found
959    }
960
961    // Now processing groups
962    for (LootGroups::const_iterator i = Groups.begin() ; i != Groups.end() ; i++ )
963        if (i->HasQuestDrop())
964            return true;
965
966    return false;
967}
968
969// True if template includes at least 1 quest drop for an active quest of the player
970bool LootTemplate::HasQuestDropForPlayer(LootTemplateMap const& store, Player const* player, uint8 groupId) const
971{
972    if (groupId)                                            // Group reference
973    {
974        if (groupId > Groups.size())
975            return false;                                   // Error message already printed at loading stage
976        return Groups[groupId-1].HasQuestDropForPlayer(player);
977    }
978
979    // Checking non-grouped entries
980    for (LootStoreItemList::const_iterator i = Entries.begin() ; i != Entries.end() ; i++ )
981    {
982        if (i->mincountOrRef < 0)                           // References processing
983        {
984            LootTemplateMap::const_iterator Referenced = store.find(-i->mincountOrRef);
985            if (Referenced == store.end() )
986                continue;                                   // Error message already printed at loading stage
987            if (Referenced->second->HasQuestDropForPlayer(store, player, i->group) )
988                return true;
989        }
990        else if ( player->HasQuestForItem(i->itemid) )
991            return true;                                    // active quest drop found
992    }
993
994    // Now checking groups
995    for (LootGroups::const_iterator i = Groups.begin(); i != Groups.end(); ++i )
996        if (i->HasQuestDrop())
997            return true;
998
999    return false;
1000}
1001
1002// Checks integrity of the template
1003void LootTemplate::Verify(LootStore const& lootstore, uint32 id) const
1004{
1005    // Checking group chances
1006    for (uint32 i=0; i < Groups.size(); ++i)
1007        Groups[i].Verify(lootstore,id,i+1);
1008
1009    // TODO: References validity checks
1010}
1011
1012void LootTemplate::CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const
1013{
1014    for(LootStoreItemList::const_iterator ieItr = Entries.begin(); ieItr != Entries.end(); ++ieItr)
1015    {
1016        if(ieItr->mincountOrRef < 0)
1017        {
1018            if(!LootTemplates_Reference.GetLootFor(-ieItr->mincountOrRef))
1019                LootTemplates_Reference.ReportNotExistedId(-ieItr->mincountOrRef);
1020            else if(ref_set)
1021                ref_set->erase(-ieItr->mincountOrRef);
1022        }
1023    }
1024
1025    for(LootGroups::const_iterator grItr = Groups.begin(); grItr != Groups.end(); ++grItr)
1026        grItr->CheckLootRefs(store,ref_set);
1027}
1028
1029void LoadLootTemplates_Creature()
1030{
1031    LootIdSet ids_set, ids_setUsed;
1032    LootTemplates_Creature.LoadAndCollectLootIds(ids_set);
1033
1034    // remove real entries and check existence loot
1035    for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i )
1036    {
1037        if(CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
1038        {
1039            if(uint32 lootid = cInfo->lootid)
1040            {
1041                if(!ids_set.count(lootid))
1042                    LootTemplates_Creature.ReportNotExistedId(lootid);
1043                else
1044                    ids_setUsed.insert(lootid);
1045            }
1046        }
1047    }
1048    for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr)
1049        ids_set.erase(*itr);
1050
1051    // output error for any still listed (not referenced from appropriate table) ids
1052    LootTemplates_Creature.ReportUnusedIds(ids_set);
1053}
1054
1055void LoadLootTemplates_Disenchant()
1056{
1057    LootIdSet ids_set, ids_setUsed;
1058    LootTemplates_Disenchant.LoadAndCollectLootIds(ids_set);
1059
1060    // remove real entries and check existence loot
1061    for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i )
1062    {
1063        if(ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype>(i))
1064        {
1065            if(uint32 lootid = proto->DisenchantID)
1066            {
1067                if(!ids_set.count(lootid))
1068                    LootTemplates_Disenchant.ReportNotExistedId(lootid);
1069                else
1070                    ids_setUsed.insert(lootid);
1071            }
1072        }
1073    }
1074    for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr)
1075        ids_set.erase(*itr);
1076    // output error for any still listed (not referenced from appropriate table) ids
1077    LootTemplates_Disenchant.ReportUnusedIds(ids_set);
1078}
1079
1080void LoadLootTemplates_Fishing()
1081{
1082    LootIdSet ids_set;
1083    LootTemplates_Fishing.LoadAndCollectLootIds(ids_set);
1084
1085    // remove real entries and check existence loot
1086    for(uint32 i = 1; i < sAreaStore.GetNumRows(); ++i )
1087    {
1088        if(AreaTableEntry const* areaEntry = sAreaStore.LookupEntry(i))
1089            if(ids_set.count(areaEntry->ID))
1090                ids_set.erase(areaEntry->ID);
1091    }
1092
1093    // output error for any still listed (not referenced from appropriate table) ids
1094    LootTemplates_Fishing.ReportUnusedIds(ids_set);
1095}
1096
1097void LoadLootTemplates_Gameobject()
1098{
1099    LootIdSet ids_set, ids_setUsed;
1100    LootTemplates_Gameobject.LoadAndCollectLootIds(ids_set);
1101
1102    // remove real entries and check existence loot
1103    for(uint32 i = 1; i < sGOStorage.MaxEntry; ++i )
1104    {
1105        if(GameObjectInfo const* gInfo = sGOStorage.LookupEntry<GameObjectInfo>(i))
1106        {
1107            if(uint32 lootid = GameObject::GetLootId(gInfo))
1108            {
1109                if(!ids_set.count(lootid))
1110                    LootTemplates_Gameobject.ReportNotExistedId(lootid);
1111                else
1112                    ids_setUsed.insert(lootid);
1113            }
1114        }
1115    }
1116    for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr)
1117        ids_set.erase(*itr);
1118
1119    // output error for any still listed (not referenced from appropriate table) ids
1120    LootTemplates_Gameobject.ReportUnusedIds(ids_set);
1121}
1122
1123void LoadLootTemplates_Item()
1124{
1125    LootIdSet ids_set;
1126    LootTemplates_Item.LoadAndCollectLootIds(ids_set);
1127
1128    // remove real entries and check existence loot
1129    for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i )
1130        if(ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype>(i))
1131            if(ids_set.count(proto->ItemId))
1132                ids_set.erase(proto->ItemId);
1133
1134    // output error for any still listed (not referenced from appropriate table) ids
1135    LootTemplates_Item.ReportUnusedIds(ids_set);
1136}
1137
1138void LoadLootTemplates_Pickpocketing()
1139{
1140    LootIdSet ids_set, ids_setUsed;
1141    LootTemplates_Pickpocketing.LoadAndCollectLootIds(ids_set);
1142
1143    // remove real entries and check existence loot
1144    for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i )
1145    {
1146        if(CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
1147        {
1148            if(uint32 lootid = cInfo->pickpocketLootId)
1149            {
1150                if(!ids_set.count(lootid))
1151                    LootTemplates_Pickpocketing.ReportNotExistedId(lootid);
1152                else
1153                    ids_setUsed.insert(lootid);
1154            }
1155        }
1156    }
1157    for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr)
1158        ids_set.erase(*itr);
1159
1160    // output error for any still listed (not referenced from appropriate table) ids
1161    LootTemplates_Pickpocketing.ReportUnusedIds(ids_set);
1162}
1163
1164void LoadLootTemplates_Prospecting()
1165{
1166    LootIdSet ids_set;
1167    LootTemplates_Prospecting.LoadAndCollectLootIds(ids_set);
1168
1169    // remove real entries and check existence loot
1170    for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i )
1171        if(ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype>(i))
1172            if(ids_set.count(proto->ItemId))
1173                ids_set.erase(proto->ItemId);
1174
1175    // output error for any still listed (not referenced from appropriate table) ids
1176    LootTemplates_Prospecting.ReportUnusedIds(ids_set);
1177}
1178
1179void LoadLootTemplates_QuestMail()
1180{
1181    LootIdSet ids_set;
1182    LootTemplates_QuestMail.LoadAndCollectLootIds(ids_set);
1183
1184    // remove real entries and check existence loot
1185    ObjectMgr::QuestMap const& questMap = objmgr.GetQuestTemplates();
1186    for(ObjectMgr::QuestMap::const_iterator itr = questMap.begin(); itr != questMap.end(); ++itr )
1187        if(ids_set.count(itr->first))
1188            ids_set.erase(itr->first);
1189
1190    // output error for any still listed (not referenced from appropriate table) ids
1191    LootTemplates_QuestMail.ReportUnusedIds(ids_set);
1192}
1193
1194void LoadLootTemplates_Skinning()
1195{
1196    LootIdSet ids_set, ids_setUsed;
1197    LootTemplates_Skinning.LoadAndCollectLootIds(ids_set);
1198
1199    // remove real entries and check existence loot
1200    for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i )
1201    {
1202        if(CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
1203        {
1204            if(uint32 lootid = cInfo->SkinLootId)
1205            {
1206                if(!ids_set.count(lootid))
1207                    LootTemplates_Skinning.ReportNotExistedId(lootid);
1208                else
1209                    ids_setUsed.insert(lootid);
1210            }
1211        }
1212    }
1213    for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr)
1214        ids_set.erase(*itr);
1215
1216    // output error for any still listed (not referenced from appropriate table) ids
1217    LootTemplates_Skinning.ReportUnusedIds(ids_set);
1218}
1219
1220void LoadLootTemplates_Reference()
1221{
1222    LootIdSet ids_set;
1223    LootTemplates_Reference.LoadAndCollectLootIds(ids_set);
1224
1225    // check references and remove used
1226    LootTemplates_Creature.CheckLootRefs(&ids_set);
1227    LootTemplates_Fishing.CheckLootRefs(&ids_set);
1228    LootTemplates_Gameobject.CheckLootRefs(&ids_set);
1229    LootTemplates_Item.CheckLootRefs(&ids_set);
1230    LootTemplates_Pickpocketing.CheckLootRefs(&ids_set);
1231    LootTemplates_Skinning.CheckLootRefs(&ids_set);
1232    LootTemplates_Disenchant.CheckLootRefs(&ids_set);
1233    LootTemplates_Prospecting.CheckLootRefs(&ids_set);
1234    LootTemplates_QuestMail.CheckLootRefs(&ids_set);
1235    LootTemplates_Reference.CheckLootRefs(&ids_set);
1236
1237    // output error for any still listed ids (not referenced from any loot table)
1238    LootTemplates_Reference.ReportUnusedIds(ids_set);
1239}
Note: See TracBrowser for help on using the browser.