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

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

[svn] *** Source: MaNGOS ***
* Fixed build extractor at Windows Vista. Author: Vladimir
* Fixed comment text and code indentifiers spelling. Author: Vladimir & Paradox.
* Access cached member lists in guild handlers instead of querying the DB. Author: Hunuza
* Small fixes in send/received packet and simple code cleanup also. Author: Vladimir
* Not output error at loading empty character_ticket table. Author: Vladimir
* Not reset display model at shapeshift aura remove if it not set at apply. Author: Arthorius
* Applied props to few files.

Original author: visagalis
Date: 2008-11-14 16:28:45-06:00

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