root/trunk/src/game/Item.cpp @ 13

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

[svn] * Proper SVN structure

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

Line 
1/*
2 * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18
19#include "Common.h"
20#include "Item.h"
21#include "ObjectMgr.h"
22#include "WorldPacket.h"
23#include "Database/DatabaseEnv.h"
24#include "ItemEnchantmentMgr.h"
25
26void AddItemsSetItem(Player*player,Item *item)
27{
28    ItemPrototype const *proto = item->GetProto();
29    uint32 setid = proto->ItemSet;
30
31    ItemSetEntry const *set = sItemSetStore.LookupEntry(setid);
32
33    if(!set)
34    {
35        sLog.outErrorDb("Item set %u for item (id %u) not found, mods not applied.",setid,proto->ItemId);
36        return;
37    }
38
39    if( set->required_skill_id && player->GetSkillValue(set->required_skill_id) < set->required_skill_value )
40        return;
41
42    ItemSetEffect *eff = NULL;
43
44    for(size_t x = 0; x < player->ItemSetEff.size(); ++x)
45    {
46        if(player->ItemSetEff[x] && player->ItemSetEff[x]->setid == setid)
47        {
48            eff = player->ItemSetEff[x];
49            break;
50        }
51    }
52
53    if(!eff)
54    {
55        eff = new ItemSetEffect;
56        memset(eff,0,sizeof(ItemSetEffect));
57        eff->setid = setid;
58
59        size_t x = 0;
60        for(; x < player->ItemSetEff.size(); x++)
61            if(!player->ItemSetEff[x])
62                break;
63
64        if(x < player->ItemSetEff.size())
65            player->ItemSetEff[x]=eff;
66        else
67            player->ItemSetEff.push_back(eff);
68    }
69
70    ++eff->item_count;
71
72    for(uint32 x=0;x<8;x++)
73    {
74        if(!set->spells [x])
75            continue;
76        //not enough for  spell
77        if(set->items_to_triggerspell[x] > eff->item_count)
78            continue;
79
80        uint32 z=0;
81        for(;z<8;z++)
82            if(eff->spells[z] && eff->spells[z]->Id==set->spells[x])
83                break;
84
85        if(z < 8)
86            continue;
87
88        //new spell
89        for(uint32 y=0;y<8;y++)
90        {
91            if(!eff->spells[y])                             // free slot
92            {
93                SpellEntry const *spellInfo = sSpellStore.LookupEntry(set->spells[x]);
94                if(!spellInfo)
95                {
96                    sLog.outError("WORLD: unknown spell id %u in items set %u effects", set->spells[x],setid);
97                    break;
98                }
99
100                // spell casted only if fit form requirement, in other case will casted at form change
101                player->ApplyEquipSpell(spellInfo,NULL,true);
102                eff->spells[y] = spellInfo;
103                break;
104            }
105        }
106    }
107}
108
109void RemoveItemsSetItem(Player*player,ItemPrototype const *proto)
110{
111    uint32 setid = proto->ItemSet;
112
113    ItemSetEntry const *set = sItemSetStore.LookupEntry(setid);
114
115    if(!set)
116    {
117        sLog.outErrorDb("Item set #%u for item #%u not found, mods not removed.",setid,proto->ItemId);
118        return;
119    }
120
121    ItemSetEffect *eff = NULL;
122    size_t setindex = 0;
123    for(;setindex < player->ItemSetEff.size(); setindex++)
124    {
125        if(player->ItemSetEff[setindex] && player->ItemSetEff[setindex]->setid == setid)
126        {
127            eff = player->ItemSetEff[setindex];
128            break;
129        }
130    }
131
132    // can be in case now enough skill requirement for set appling but set has been appliend when skill requirement not enough
133    if(!eff)
134        return;
135
136    --eff->item_count;
137
138    for(uint32 x=0;x<8;x++)
139    {
140        if(!set->spells[x])
141            continue;
142
143        // enough for spell
144        if(set->items_to_triggerspell[x] <= eff->item_count)
145            continue;
146
147        for(uint32 z=0;z<8;z++)
148        {
149            if(eff->spells[z] && eff->spells[z]->Id==set->spells[x])
150            {
151                // spell can be not active if not fit form requirement
152                player->ApplyEquipSpell(eff->spells[z],NULL,false);
153                eff->spells[z]=NULL;
154                break;
155            }
156        }
157    }
158
159    if(!eff->item_count)                                    //all items of a set were removed
160    {
161        assert(eff == player->ItemSetEff[setindex]);
162        delete eff;
163        player->ItemSetEff[setindex] = NULL;
164    }
165}
166
167bool ItemCanGoIntoBag(ItemPrototype const *pProto, ItemPrototype const *pBagProto)
168{
169    if(!pProto || !pBagProto)
170        return false;
171
172    switch(pBagProto->Class)
173    {
174        case ITEM_CLASS_CONTAINER:
175            switch(pBagProto->SubClass)
176            {
177                case ITEM_SUBCLASS_CONTAINER:
178                    return true;
179                case ITEM_SUBCLASS_SOUL_CONTAINER:
180                    if(!(pProto->BagFamily & BAG_FAMILY_MASK_SHARDS))
181                        return false;
182                    return true;
183                case ITEM_SUBCLASS_HERB_CONTAINER:
184                    if(!(pProto->BagFamily & BAG_FAMILY_MASK_HERBS))
185                        return false;
186                    return true;
187                case ITEM_SUBCLASS_ENCHANTING_CONTAINER:
188                    if(!(pProto->BagFamily & BAG_FAMILY_MASK_ENCHANTING_SUPP))
189                        return false;
190                    return true;
191                case ITEM_SUBCLASS_MINING_CONTAINER:
192                    if(!(pProto->BagFamily & BAG_FAMILY_MASK_MINING_SUPP))
193                        return false;
194                    return true;
195                case ITEM_SUBCLASS_ENGINEERING_CONTAINER:
196                    if(!(pProto->BagFamily & BAG_FAMILY_MASK_ENGINEERING_SUPP))
197                        return false;
198                    return true;
199                case ITEM_SUBCLASS_GEM_CONTAINER:
200                    if(!(pProto->BagFamily & BAG_FAMILY_MASK_GEMS))
201                        return false;
202                    return true;
203                case ITEM_SUBCLASS_LEATHERWORKING_CONTAINER:
204                    if(!(pProto->BagFamily & BAG_FAMILY_MASK_LEATHERWORKING_SUPP))
205                        return false;
206                    return true;
207                default:
208                    return false;
209            }
210        case ITEM_CLASS_QUIVER:
211            switch(pBagProto->SubClass)
212            {
213                case ITEM_SUBCLASS_QUIVER:
214                    if(!(pProto->BagFamily & BAG_FAMILY_MASK_ARROWS))
215                        return false;
216                    return true;
217                case ITEM_SUBCLASS_AMMO_POUCH:
218                    if(!(pProto->BagFamily & BAG_FAMILY_MASK_BULLETS))
219                        return false;
220                    return true;
221                default:
222                    return false;
223            }
224    }
225    return false;
226}
227
228Item::Item( )
229{
230    m_objectType |= TYPEMASK_ITEM;
231    m_objectTypeId = TYPEID_ITEM;
232                                                            // 2.3.2 - 0x18
233    m_updateFlag = (UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID);
234
235    m_valuesCount = ITEM_END;
236    m_slot = 0;
237    uState = ITEM_NEW;
238    uQueuePos = -1;
239    m_container = NULL;
240    m_lootGenerated = false;
241    mb_in_trade = false;
242}
243
244bool Item::Create( uint32 guidlow, uint32 itemid, Player const* owner)
245{
246    Object::_Create( guidlow, 0, HIGHGUID_ITEM );
247
248    SetUInt32Value(OBJECT_FIELD_ENTRY, itemid);
249    SetFloatValue(OBJECT_FIELD_SCALE_X, 1.0f);
250
251    SetUInt64Value(ITEM_FIELD_OWNER, owner ? owner->GetGUID() : 0);
252    SetUInt64Value(ITEM_FIELD_CONTAINED, owner ? owner->GetGUID() : 0);
253
254    ItemPrototype const *itemProto = objmgr.GetItemPrototype(itemid);
255    if(!itemProto)
256        return false;
257
258    SetUInt32Value(ITEM_FIELD_STACK_COUNT, 1);
259    SetUInt32Value(ITEM_FIELD_MAXDURABILITY, itemProto->MaxDurability);
260    SetUInt32Value(ITEM_FIELD_DURABILITY, itemProto->MaxDurability);
261
262    for(int i = 0; i < 5; ++i)
263        SetSpellCharges(i,itemProto->Spells[i].SpellCharges);
264
265    SetUInt32Value(ITEM_FIELD_FLAGS, itemProto->Flags);
266    SetUInt32Value(ITEM_FIELD_DURATION, abs(itemProto->Duration));
267
268    return true;
269}
270
271void Item::UpdateDuration(Player* owner, uint32 diff)
272{
273    if (!GetUInt32Value(ITEM_FIELD_DURATION))
274        return;
275
276    sLog.outDebug("Item::UpdateDuration Item (Entry: %u Duration %u Diff %u)",GetEntry(),GetUInt32Value(ITEM_FIELD_DURATION),diff);
277
278    if (GetUInt32Value(ITEM_FIELD_DURATION)<=diff)
279    {
280        owner->DestroyItem(GetBagSlot(), GetSlot(), true);
281        return;
282    }
283
284    SetUInt32Value(ITEM_FIELD_DURATION, GetUInt32Value(ITEM_FIELD_DURATION) - diff);
285    SetState(ITEM_CHANGED);                                 // save new time in database
286}
287
288void Item::SaveToDB()
289{
290    uint32 guid = GetGUIDLow();
291    switch (uState)
292    {
293        case ITEM_NEW:
294        {
295            CharacterDatabase.PExecute( "DELETE FROM item_instance WHERE guid = '%u'", guid );
296            std::ostringstream ss;
297            ss << "INSERT INTO item_instance (guid,owner_guid,data) VALUES (" << guid << "," << GUID_LOPART(GetOwnerGUID()) << ",'";
298            for(uint16 i = 0; i < m_valuesCount; i++ )
299                ss << GetUInt32Value(i) << " ";
300            ss << "' )";
301            CharacterDatabase.Execute( ss.str().c_str() );
302        } break;
303        case ITEM_CHANGED:
304        {
305            std::ostringstream ss;
306            ss << "UPDATE item_instance SET data = '";
307            for(uint16 i = 0; i < m_valuesCount; i++ )
308                ss << GetUInt32Value(i) << " ";
309            ss << "', owner_guid = '" << GUID_LOPART(GetOwnerGUID()) << "' WHERE guid = '" << guid << "'";
310
311            CharacterDatabase.Execute( ss.str().c_str() );
312
313            if(HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))
314                CharacterDatabase.PExecute("UPDATE character_gifts SET guid = '%u' WHERE item_guid = '%u'", GUID_LOPART(GetOwnerGUID()),GetGUIDLow());
315        } break;
316        case ITEM_REMOVED:
317        {
318            if (GetUInt32Value(ITEM_FIELD_ITEM_TEXT_ID) > 0 )
319                CharacterDatabase.PExecute("DELETE FROM item_text WHERE id = '%u'", GetUInt32Value(ITEM_FIELD_ITEM_TEXT_ID));
320            CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", guid);
321            if(HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))
322                CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE item_guid = '%u'", GetGUIDLow());
323            delete this;
324            return;
325        }
326        case ITEM_UNCHANGED:
327            break;
328    }
329    SetState(ITEM_UNCHANGED);
330}
331
332bool Item::LoadFromDB(uint32 guid, uint64 owner_guid, QueryResult *result)
333{
334    // create item before any checks for store correct guid
335    // and allow use "FSetState(ITEM_REMOVED); SaveToDB();" for deleting item from DB
336    Object::_Create(guid, 0, HIGHGUID_ITEM);
337
338    bool delete_result = false;
339    if(!result)
340    {
341        result = CharacterDatabase.PQuery("SELECT data FROM item_instance WHERE guid = '%u'", guid);
342        delete_result = true;
343    }
344
345    if (!result)
346    {
347        sLog.outError("ERROR: Item (GUID: %u owner: %u) not found in table `item_instance`, can't load. ",guid,GUID_LOPART(owner_guid));
348        return false;
349    }
350
351    Field *fields = result->Fetch();
352
353    if(!LoadValues(fields[0].GetString()))
354    {
355        sLog.outError("ERROR: Item #%d have broken data in `data` field. Can't be loaded.",guid);
356        if (delete_result) delete result;
357        return false;
358    }
359
360    bool need_save = false;                                 // need explicit save data at load fixes
361
362    // overwrite possible wrong/corrupted guid
363    uint64 new_item_guid = MAKE_NEW_GUID(guid,0, HIGHGUID_ITEM);
364    if(GetUInt64Value(OBJECT_FIELD_GUID) != new_item_guid)
365    {
366        SetUInt64Value(OBJECT_FIELD_GUID, MAKE_NEW_GUID(guid,0, HIGHGUID_ITEM));
367        need_save = true;
368    }
369
370    if (delete_result) delete result;
371
372    ItemPrototype const* proto = GetProto();
373    if(!proto)
374        return false;
375
376    // recalculate suffix factor
377    if(GetItemRandomPropertyId() < 0)
378    {
379        if(UpdateItemSuffixFactor())
380            need_save = true;
381    }
382
383    // Remove bind flag for items vs NO_BIND set
384    if (IsSoulBound() && proto->Bonding == NO_BIND)
385    {
386        ApplyModFlag(ITEM_FIELD_FLAGS,ITEM_FLAGS_BINDED, false);
387        need_save = true;
388    }
389
390    // update duration if need, and remove if not need
391    if((proto->Duration==0) != (GetUInt32Value(ITEM_FIELD_DURATION)==0))
392    {
393        SetUInt32Value(ITEM_FIELD_DURATION,abs(proto->Duration));
394        need_save = true;
395    }
396
397    // set correct owner
398    if(owner_guid != 0 && GetOwnerGUID() != owner_guid)
399    {
400        SetOwnerGUID(owner_guid);
401        need_save = true;
402    }
403
404    if(need_save)                                           // normal item changed state set not work at loading
405    {
406        std::ostringstream ss;
407        ss << "UPDATE item_instance SET data = '";
408        for(uint16 i = 0; i < m_valuesCount; i++ )
409            ss << GetUInt32Value(i) << " ";
410        ss << "', owner_guid = '" << GUID_LOPART(GetOwnerGUID()) << "' WHERE guid = '" << guid << "'";
411
412        CharacterDatabase.Execute( ss.str().c_str() );
413    }
414
415    return true;
416}
417
418void Item::DeleteFromDB()
419{
420    CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'",GetGUIDLow());
421}
422
423void Item::DeleteFromInventoryDB()
424{
425    CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'",GetGUIDLow());
426}
427
428ItemPrototype const *Item::GetProto() const
429{
430    return objmgr.GetItemPrototype(GetUInt32Value(OBJECT_FIELD_ENTRY));
431}
432
433Player* Item::GetOwner()const
434{
435    return objmgr.GetPlayer(GetOwnerGUID());
436}
437
438uint32 Item::GetSkill()
439{
440    const static uint32 item_weapon_skills[MAX_ITEM_SUBCLASS_WEAPON] =
441    {
442        SKILL_AXES,     SKILL_2H_AXES,  SKILL_BOWS,          SKILL_GUNS,      SKILL_MACES,
443        SKILL_2H_MACES, SKILL_POLEARMS, SKILL_SWORDS,        SKILL_2H_SWORDS, 0,
444        SKILL_STAVES,   0,              0,                   SKILL_UNARMED,   0,
445        SKILL_DAGGERS,  SKILL_THROWN,   SKILL_ASSASSINATION, SKILL_CROSSBOWS, SKILL_WANDS,
446        SKILL_FISHING
447    };
448
449    const static uint32 item_armor_skills[MAX_ITEM_SUBCLASS_ARMOR] =
450    {
451        0,SKILL_CLOTH,SKILL_LEATHER,SKILL_MAIL,SKILL_PLATE_MAIL,0,SKILL_SHIELD,0,0,0
452    };
453
454    ItemPrototype const* proto = GetProto();
455
456    switch (proto->Class)
457    {
458        case ITEM_CLASS_WEAPON:
459            if( proto->SubClass >= MAX_ITEM_SUBCLASS_WEAPON )
460                return 0;
461            else
462                return item_weapon_skills[proto->SubClass];
463
464        case ITEM_CLASS_ARMOR:
465            if( proto->SubClass >= MAX_ITEM_SUBCLASS_ARMOR )
466                return 0;
467            else
468                return item_armor_skills[proto->SubClass];
469
470        default:
471            return 0;
472    }
473}
474
475uint32 Item::GetSpell()
476{
477    ItemPrototype const* proto = GetProto();
478
479    switch (proto->Class)
480    {
481        case ITEM_CLASS_WEAPON:
482            switch (proto->SubClass)
483            {
484                case ITEM_SUBCLASS_WEAPON_AXE:     return  196;
485                case ITEM_SUBCLASS_WEAPON_AXE2:    return  197;
486                case ITEM_SUBCLASS_WEAPON_BOW:     return  264;
487                case ITEM_SUBCLASS_WEAPON_GUN:     return  266;
488                case ITEM_SUBCLASS_WEAPON_MACE:    return  198;
489                case ITEM_SUBCLASS_WEAPON_MACE2:   return  199;
490                case ITEM_SUBCLASS_WEAPON_POLEARM: return  200;
491                case ITEM_SUBCLASS_WEAPON_SWORD:   return  201;
492                case ITEM_SUBCLASS_WEAPON_SWORD2:  return  202;
493                case ITEM_SUBCLASS_WEAPON_STAFF:   return  227;
494                case ITEM_SUBCLASS_WEAPON_DAGGER:  return 1180;
495                case ITEM_SUBCLASS_WEAPON_THROWN:  return 2567;
496                case ITEM_SUBCLASS_WEAPON_SPEAR:   return 3386;
497                case ITEM_SUBCLASS_WEAPON_CROSSBOW:return 5011;
498                case ITEM_SUBCLASS_WEAPON_WAND:    return 5009;
499                default: return 0;
500            }
501        case ITEM_CLASS_ARMOR:
502            switch(proto->SubClass)
503            {
504                case ITEM_SUBCLASS_ARMOR_CLOTH:    return 9078;
505                case ITEM_SUBCLASS_ARMOR_LEATHER:  return 9077;
506                case ITEM_SUBCLASS_ARMOR_MAIL:     return 8737;
507                case ITEM_SUBCLASS_ARMOR_PLATE:    return  750;
508                case ITEM_SUBCLASS_ARMOR_SHIELD:   return 9116;
509                default: return 0;
510            }
511    }
512    return 0;
513}
514
515int32 Item::GenerateItemRandomPropertyId(uint32 item_id)
516{
517    ItemPrototype const *itemProto = sItemStorage.LookupEntry<ItemPrototype>(item_id);
518
519    if(!itemProto)
520        return 0;
521
522    // item must have one from this field values not null if it can have random enchantments
523    if((!itemProto->RandomProperty) && (!itemProto->RandomSuffix))
524        return 0;
525
526    // item can have not null only one from field values
527    if((itemProto->RandomProperty) && (itemProto->RandomSuffix))
528    {
529        sLog.outErrorDb("Item template %u have RandomProperty==%u and RandomSuffix==%u, but must have one from field =0",itemProto->ItemId,itemProto->RandomProperty,itemProto->RandomSuffix);
530        return 0;
531    }
532
533    // RandomProperty case
534    if(itemProto->RandomProperty)
535    {
536        uint32 randomPropId = GetItemEnchantMod(itemProto->RandomProperty);
537        ItemRandomPropertiesEntry const *random_id = sItemRandomPropertiesStore.LookupEntry(randomPropId);
538        if(!random_id)
539        {
540            sLog.outErrorDb("Enchantment id #%u used but it doesn't have records in 'ItemRandomProperties.dbc'",randomPropId);
541            return 0;
542        }
543
544        return random_id->ID;
545    }
546    // RandomSuffix case
547    else
548    {
549        uint32 randomPropId = GetItemEnchantMod(itemProto->RandomSuffix);
550        ItemRandomSuffixEntry const *random_id = sItemRandomSuffixStore.LookupEntry(randomPropId);
551        if(!random_id)
552        {
553            sLog.outErrorDb("Enchantment id #%u used but it doesn't have records in sItemRandomSuffixStore.",randomPropId);
554            return 0;
555        }
556
557        return -int32(random_id->ID);
558    }
559}
560
561void Item::SetItemRandomProperties(int32 randomPropId)
562{
563    if(!randomPropId)
564        return;
565
566    if(randomPropId > 0)
567    {
568        ItemRandomPropertiesEntry const *item_rand = sItemRandomPropertiesStore.LookupEntry(randomPropId);
569        if(item_rand)
570        {
571            if(GetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID) != int32(item_rand->ID))
572            {
573                SetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID,item_rand->ID);
574                SetState(ITEM_CHANGED);
575            }
576            for(uint32 i = PROP_ENCHANTMENT_SLOT_2; i < PROP_ENCHANTMENT_SLOT_2 + 3; ++i)
577                SetEnchantment(EnchantmentSlot(i),item_rand->enchant_id[i - PROP_ENCHANTMENT_SLOT_2],0,0);
578        }
579    }
580    else
581    {
582        ItemRandomSuffixEntry const *item_rand = sItemRandomSuffixStore.LookupEntry(-randomPropId);
583        if(item_rand)
584        {
585            if( GetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID) != -int32(item_rand->ID) ||
586                !GetItemSuffixFactor())
587            {
588                SetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID,-int32(item_rand->ID));
589                UpdateItemSuffixFactor();
590                SetState(ITEM_CHANGED);
591            }
592
593            for(uint32 i = PROP_ENCHANTMENT_SLOT_0; i < PROP_ENCHANTMENT_SLOT_0 + 3; ++i)
594                SetEnchantment(EnchantmentSlot(i),item_rand->enchant_id[i - PROP_ENCHANTMENT_SLOT_0],0,0);
595        }
596    }
597}
598
599bool Item::UpdateItemSuffixFactor()
600{
601    uint32 suffixFactor = GenerateEnchSuffixFactor(GetEntry());
602    if(GetItemSuffixFactor()==suffixFactor)
603        return false;
604    SetUInt32Value(ITEM_FIELD_PROPERTY_SEED,suffixFactor);
605    return true;
606}
607
608void Item::SetState(ItemUpdateState state, Player *forplayer)
609{
610    if (uState == ITEM_NEW && state == ITEM_REMOVED)
611    {
612        // pretend the item never existed
613        RemoveFromUpdateQueueOf(forplayer);
614        delete this;
615        return;
616    }
617
618    if (state != ITEM_UNCHANGED)
619    {
620        // new items must stay in new state until saved
621        if (uState != ITEM_NEW) uState = state;
622        AddToUpdateQueueOf(forplayer);
623    }
624    else
625    {
626        // unset in queue
627        // the item must be removed from the queue manually
628        uQueuePos = -1;
629        uState = ITEM_UNCHANGED;
630    }
631}
632
633void Item::AddToUpdateQueueOf(Player *player)
634{
635    if (IsInUpdateQueue()) return;
636
637    if (!player)
638    {
639        player = GetOwner();
640        if (!player)
641        {
642            sLog.outError("Item::AddToUpdateQueueOf - GetPlayer didn't find a player matching owner's guid (%u)!", GUID_LOPART(GetOwnerGUID()));
643            return;
644        }
645    }
646
647    if (player->GetGUID() != GetOwnerGUID())
648    {
649        sLog.outError("Item::AddToUpdateQueueOf - Owner's guid (%u) and player's guid (%u) don't match!", GUID_LOPART(GetOwnerGUID()), player->GetGUIDLow());
650        return;
651    }
652
653    if (player->m_itemUpdateQueueBlocked) return;
654
655    player->m_itemUpdateQueue.push_back(this);
656    uQueuePos = player->m_itemUpdateQueue.size()-1;
657}
658
659void Item::RemoveFromUpdateQueueOf(Player *player)
660{
661    if (!IsInUpdateQueue()) return;
662
663    if (!player)
664    {
665        player = GetOwner();
666        if (!player)
667        {
668            sLog.outError("Item::RemoveFromUpdateQueueOf - GetPlayer didn't find a player matching owner's guid (%u)!", GUID_LOPART(GetOwnerGUID()));
669            return;
670        }
671    }
672
673    if (player->GetGUID() != GetOwnerGUID())
674    {
675        sLog.outError("Item::RemoveFromUpdateQueueOf - Owner's guid (%u) and player's guid (%u) don't match!", GUID_LOPART(GetOwnerGUID()), player->GetGUIDLow());
676        return;
677    }
678
679    if (player->m_itemUpdateQueueBlocked) return;
680
681    player->m_itemUpdateQueue[uQueuePos] = NULL;
682    uQueuePos = -1;
683}
684
685uint8 Item::GetBagSlot() const
686{
687    return m_container ? m_container->GetSlot() : uint8(INVENTORY_SLOT_BAG_0);
688}
689
690bool Item::IsEquipped() const
691{
692    return !IsInBag() && m_slot < EQUIPMENT_SLOT_END;
693}
694
695bool Item::CanBeTraded() const
696{
697    if(IsSoulBound())
698        return false;
699    if(IsBag() && (Player::IsBagPos(GetPos()) || !((Bag const*)this)->IsEmpty()) )
700        return false;
701
702    if(Player* owner = GetOwner())
703    {
704        if(owner->CanUnequipItem(GetPos(),false) !=  EQUIP_ERR_OK )
705            return false;
706        if(owner->GetLootGUID()==GetGUID())
707            return false;
708    }
709
710    if (IsBoundByEnchant())
711        return false;
712
713    return true;
714}
715
716bool Item::IsBoundByEnchant() const
717{
718    // Check all enchants for soulbound
719    for(uint32 enchant_slot = PERM_ENCHANTMENT_SLOT; enchant_slot < MAX_ENCHANTMENT_SLOT; ++enchant_slot)
720    {
721        uint32 enchant_id = GetEnchantmentId(EnchantmentSlot(enchant_slot));
722        if(!enchant_id)
723            continue;
724
725        SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
726        if(!enchantEntry)
727            continue;
728
729        if(enchantEntry->slot & ENCHANTMENT_CAN_SOULBOUND)
730            return true;
731    }
732    return false;
733}
734
735bool Item::IsFitToSpellRequirements(SpellEntry const* spellInfo) const
736{
737    ItemPrototype const* proto = GetProto();
738
739    if (spellInfo->EquippedItemClass != -1)                 // -1 == any item class
740    {
741        if(spellInfo->EquippedItemClass != int32(proto->Class))
742            return false;                                   //  wrong item class
743
744        if(spellInfo->EquippedItemSubClassMask != 0)        // 0 == any subclass
745        {
746            if((spellInfo->EquippedItemSubClassMask & (1 << proto->SubClass)) == 0)
747                return false;                               // subclass not present in mask
748        }
749    }
750
751    if(spellInfo->EquippedItemInventoryTypeMask != 0)       // 0 == any inventory type
752    {
753        if((spellInfo->EquippedItemInventoryTypeMask  & (1 << proto->InventoryType)) == 0)
754            return false;                                   // inventory type not present in mask
755    }
756
757    return true;
758}
759
760void Item::SetEnchantment(EnchantmentSlot slot, uint32 id, uint32 duration, uint32 charges)
761{
762    // Better lost small time at check in comparison lost time at item save to DB.
763    if( GetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_ID_OFFSET)==id &&
764        GetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET)==duration &&
765        GetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_CHARGES_OFFSET)==charges )
766        return;
767
768    SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_ID_OFFSET,id);
769    SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET,duration);
770    SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_CHARGES_OFFSET,charges);
771    SetState(ITEM_CHANGED);
772}
773
774void Item::SetEnchantmentDuration(EnchantmentSlot slot, uint32 duration)
775{
776    if(GetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET)==duration)
777        return;
778
779    SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET,duration);
780    SetState(ITEM_CHANGED);
781}
782
783void Item::SetEnchantmentCharges(EnchantmentSlot slot, uint32 charges)
784{
785    SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_CHARGES_OFFSET,charges);
786    SetState(ITEM_CHANGED);
787}
788
789void Item::ClearEnchantment(EnchantmentSlot slot)
790{
791    if(!GetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_ID_OFFSET))
792        return;
793
794    for(int x=0;x<3;x++)
795        SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + x,0);
796    SetState(ITEM_CHANGED);
797}
798
799bool Item::GemsFitSockets() const
800{
801    bool fits = true;
802    for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
803    {
804        uint8 SocketColor = GetProto()->Socket[enchant_slot-SOCK_ENCHANTMENT_SLOT].Color;
805
806        uint32 enchant_id = GetEnchantmentId(EnchantmentSlot(enchant_slot));
807        if(!enchant_id)
808        {
809            if(SocketColor) fits &= false;
810            continue;
811        }
812
813        SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
814        if(!enchantEntry)
815        {
816            if(SocketColor) fits &= false;
817            continue;
818        }
819
820        uint8 GemColor = 0;
821
822        uint32 gemid = enchantEntry->GemID;
823        if(gemid)
824        {
825            ItemPrototype const* gemProto = sItemStorage.LookupEntry<ItemPrototype>(gemid);
826            if(gemProto)
827            {
828                GemPropertiesEntry const* gemProperty = sGemPropertiesStore.LookupEntry(gemProto->GemProperties);
829                if(gemProperty)
830                    GemColor = gemProperty->color;
831            }
832        }
833
834        fits &= (GemColor & SocketColor) ? true : false;
835    }
836    return fits;
837}
838
839uint8 Item::GetGemCountWithID(uint32 GemID) const
840{
841    uint8 count = 0;
842    for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
843    {
844        uint32 enchant_id = GetEnchantmentId(EnchantmentSlot(enchant_slot));
845        if(!enchant_id)
846            continue;
847
848        SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
849        if(!enchantEntry)
850            continue;
851
852        if(GemID == enchantEntry->GemID)
853            ++count;
854    }
855    return count;
856}
857
858bool Item::IsLimitedToAnotherMapOrZone( uint32 cur_mapId, uint32 cur_zoneId) const
859{
860    ItemPrototype const* proto = GetProto();
861    return proto && (proto->Map && proto->Map != cur_mapId || proto->Area && proto->Area != cur_zoneId );
862}
863
864// Though the client has the information in the item's data field,
865// we have to send SMSG_ITEM_TIME_UPDATE to display the remaining
866// time.
867void Item::SendTimeUpdate(Player* owner)
868{
869    if (!GetUInt32Value(ITEM_FIELD_DURATION))
870        return;
871
872    WorldPacket data(SMSG_ITEM_TIME_UPDATE, (8+4));
873    data << (uint64)GetGUID();
874    data << (uint32)GetUInt32Value(ITEM_FIELD_DURATION);
875    owner->GetSession()->SendPacket(&data);
876}
877
878Item* Item::CreateItem( uint32 item, uint32 count, Player const* player )
879{
880    if ( count < 1 )
881        return NULL;                                        //don't create item at zero count
882
883    ItemPrototype const *pProto = objmgr.GetItemPrototype( item );
884    if( pProto )
885    {
886        if ( count > pProto->Stackable )
887            count = pProto->Stackable;
888
889        assert(count !=0 && "pProto->Stackable==0 but checked at loading already");
890
891        Item *pItem = NewItemOrBag( pProto );
892        if( pItem->Create(objmgr.GenerateLowGuid(HIGHGUID_ITEM), item, player) )
893        {
894            pItem->SetCount( count );
895            return pItem;
896        }
897        else
898            delete pItem;
899    }
900    return NULL;
901}
902
903Item* Item::CloneItem( uint32 count, Player const* player ) const
904{
905    Item* newItem = CreateItem( GetEntry(), count, player );
906    if(!newItem)
907        return NULL;
908
909    newItem->SetUInt32Value( ITEM_FIELD_CREATOR,      GetUInt32Value( ITEM_FIELD_CREATOR ) );
910    newItem->SetUInt32Value( ITEM_FIELD_GIFTCREATOR,  GetUInt32Value( ITEM_FIELD_GIFTCREATOR ) );
911    newItem->SetUInt32Value( ITEM_FIELD_FLAGS,        GetUInt32Value( ITEM_FIELD_FLAGS ) );
912    newItem->SetUInt32Value( ITEM_FIELD_DURATION,     GetUInt32Value( ITEM_FIELD_DURATION ) );
913    newItem->SetItemRandomProperties(GetItemRandomPropertyId());
914    return newItem;
915}
Note: See TracBrowser for help on using the browser.