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

Revision 230, 44.6 kB (checked in by yumileroy, 17 years ago)

[svn] *** Source: MaNGOS ***
* Implement localization of creature/gameobject name that say/yell. Author: evilstar (rewrited by: Vladimir)
* Fix auth login queue. Author: Derex
* Allowed switching INVTYPE_HOLDABLE items during combat, used correct spells for triggering global cooldown at weapon switch. Author: mobel/simak
* Fixed some format arg type/value pairs. Other warnings. Author: Vladimir
* [238_world.sql] Allow have team dependent graveyards at entrance map for instances. Author: Vladimir

NOTE:
Entrance map graveyards selected by same way as local (by distance from entrance) Until DB support will work in old way base at current DB data.

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