root/trunk/src/game/Pet.cpp @ 37

Revision 37, 58.9 kB (checked in by yumileroy, 17 years ago)

[svn] * svn:eol-style native set on all files that need it

Original author: Neo2003
Date: 2008-10-11 14:16:25-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 "Database/DatabaseEnv.h"
21#include "Log.h"
22#include "WorldSession.h"
23#include "WorldPacket.h"
24#include "ObjectMgr.h"
25#include "SpellMgr.h"
26#include "Pet.h"
27#include "MapManager.h"
28#include "Formulas.h"
29#include "SpellAuras.h"
30#include "CreatureAI.h"
31#include "Unit.h"
32#include "Util.h"
33
34char const* petTypeSuffix[MAX_PET_TYPE] =
35{
36    "'s Minion",                                            // SUMMON_PET
37    "'s Pet",                                               // HUNTER_PET
38    "'s Guardian",                                          // GUARDIAN_PET
39    "'s Companion"                                          // MINI_PET
40};
41
42//numbers represent minutes * 100 while happy (you get 100 loyalty points per min while happy)
43uint32 const LevelUpLoyalty[6] =
44{
45    5500,
46    11500,
47    17000,
48    23500,
49    31000,
50    39500,
51};
52
53uint32 const LevelStartLoyalty[6] =
54{
55    2000,
56    4500,
57    7000,
58    10000,
59    13500,
60    17500,
61};
62
63Pet::Pet(PetType type) : Creature()
64{
65    m_isPet = true;
66    m_name = "Pet";
67    m_petType = type;
68
69    m_removed = false;
70    m_regenTimer = 4000;
71    m_happinessTimer = 7500;
72    m_loyaltyTimer = 12000;
73    m_duration = 0;
74    m_bonusdamage = 0;
75
76    m_loyaltyPoints = 0;
77    m_TrainingPoints = 0;
78    m_resetTalentsCost = 0;
79    m_resetTalentsTime = 0;
80
81    m_auraUpdateMask = 0;
82
83    // pets always have a charminfo, even if they are not actually charmed
84    CharmInfo* charmInfo = InitCharmInfo(this);
85
86    if(type == MINI_PET)                                    // always passive
87        charmInfo->SetReactState(REACT_PASSIVE);
88    else if(type == GUARDIAN_PET)                           // always aggressive
89        charmInfo->SetReactState(REACT_AGGRESSIVE);
90
91    m_spells.clear();
92    m_Auras.clear();
93    m_CreatureSpellCooldowns.clear();
94    m_CreatureCategoryCooldowns.clear();
95    m_autospells.clear();
96    m_declinedname = NULL;
97}
98
99Pet::~Pet()
100{
101    if(m_uint32Values)                                      // only for fully created Object
102    {
103        for (PetSpellMap::iterator i = m_spells.begin(); i != m_spells.end(); ++i)
104            delete i->second;
105        ObjectAccessor::Instance().RemoveObject(this);
106    }
107
108    delete m_declinedname;
109}
110
111void Pet::AddToWorld()
112{
113    ///- Register the pet for guid lookup
114    if(!IsInWorld()) ObjectAccessor::Instance().AddObject(this);
115    Unit::AddToWorld();
116}
117
118void Pet::RemoveFromWorld()
119{
120    ///- Remove the pet from the accessor
121    if(IsInWorld()) ObjectAccessor::Instance().RemoveObject(this);
122    ///- Don't call the function for Creature, normal mobs + totems go in a different storage
123    Unit::RemoveFromWorld();
124}
125
126bool Pet::LoadPetFromDB( Unit* owner, uint32 petentry, uint32 petnumber, bool current )
127{
128    uint32 ownerid = owner->GetGUIDLow();
129
130    QueryResult *result;
131
132    if(petnumber)
133        // known petnumber entry                  0   1      2      3        4      5    6           7              8        9           10    11    12       13         14       15            16      17              18        19                 20                 21              22
134        result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND id = '%u'",ownerid, petnumber);
135    else if(current)
136        // current pet (slot 0)                   0   1      2      3        4      5    6           7              8        9           10    11    12       13         14       15            16      17              18        19                 20                 21              22
137        result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND slot = '0'",ownerid );
138    else if(petentry)
139        // known petentry entry (unique for summoned pet, but non unique for hunter pet (only from current or not stabled pets)
140        //                                        0   1      2      3        4      5    6           7              8        9           10    11    12       13         14       15            16      17              18        19                 20                 21              22
141        result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND entry = '%u' AND (slot = '0' OR slot = '3') ",ownerid, petentry );
142    else
143        // any current or other non-stabled pet (for hunter "call pet")
144        //                                        0   1      2      3        4      5    6           7              8        9           10    11    12       13         14       15            16      17              18        19                 20                 21              22
145        result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND (slot = '0' OR slot = '3') ",ownerid);
146
147    if(!result)
148        return false;
149
150    Field *fields = result->Fetch();
151
152    // update for case of current pet "slot = 0"
153    petentry = fields[1].GetUInt32();
154    if(!petentry)
155    {
156        delete result;
157        return false;
158    }
159
160    uint32 summon_spell_id = fields[21].GetUInt32();
161    SpellEntry const* spellInfo = sSpellStore.LookupEntry(summon_spell_id);
162
163    bool is_temporary_summoned = spellInfo && GetSpellDuration(spellInfo) > 0;
164
165    // check temporary summoned pets like mage water elemental
166    if(current && is_temporary_summoned)
167    {
168        delete result;
169        return false;
170    }
171
172    Map *map = owner->GetMap();
173    uint32 guid=objmgr.GenerateLowGuid(HIGHGUID_PET);
174    uint32 pet_number = fields[0].GetUInt32();
175    if(!Create(guid, map, petentry, pet_number))
176    {
177        delete result;
178        return false;
179    }
180
181    float px, py, pz;
182    owner->GetClosePoint(px, py, pz,GetObjectSize(),PET_FOLLOW_DIST,PET_FOLLOW_ANGLE);
183
184    Relocate(px, py, pz, owner->GetOrientation());
185
186    if(!IsPositionValid())
187    {
188        sLog.outError("ERROR: Pet (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %d Y: ^%d)", GetGUIDLow(), GetEntry(), GetPositionX(), GetPositionY());
189        delete result;
190        return false;
191    }
192
193    setPetType(PetType(fields[22].GetUInt8()));
194    SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,owner->getFaction());
195    SetUInt32Value(UNIT_CREATED_BY_SPELL, summon_spell_id);
196
197    CreatureInfo const *cinfo = GetCreatureInfo();
198    if(cinfo->type == CREATURE_TYPE_CRITTER)
199    {
200        AIM_Initialize();
201        map->Add((Creature*)this);
202        delete result;
203        return true;
204    }
205    if(getPetType()==HUNTER_PET || getPetType()==SUMMON_PET && cinfo->type == CREATURE_TYPE_DEMON && owner->getClass() == CLASS_WARLOCK)
206        m_charmInfo->SetPetNumber(pet_number, true);
207    else
208        m_charmInfo->SetPetNumber(pet_number, false);
209    SetUInt64Value(UNIT_FIELD_SUMMONEDBY, owner->GetGUID());
210    SetDisplayId(fields[3].GetUInt32());
211    SetNativeDisplayId(fields[3].GetUInt32());
212    uint32 petlevel=fields[4].GetUInt32();
213    SetUInt32Value(UNIT_NPC_FLAGS , 0);
214    SetName(fields[11].GetString());
215
216    switch(getPetType())
217    {
218
219        case SUMMON_PET:
220            petlevel=owner->getLevel();
221
222            SetUInt32Value(UNIT_FIELD_BYTES_0,2048);
223            SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
224                                                            // this enables popup window (pet dismiss, cancel)
225            break;
226        case HUNTER_PET:
227            SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100);
228            SetByteValue(UNIT_FIELD_BYTES_1, 1, fields[8].GetUInt32());
229            SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE );
230            SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 );
231
232            if(fields[12].GetBool())
233                SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_NOT_ALLOWED);
234            else
235                SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_ALLOWED);
236
237            SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
238                                                            // this enables popup window (pet abandon, cancel)
239            SetTP(fields[9].GetInt32());
240            SetMaxPower(POWER_HAPPINESS,GetCreatePowers(POWER_HAPPINESS));
241            SetPower(   POWER_HAPPINESS,fields[15].GetUInt32());
242            setPowerType(POWER_FOCUS);
243            break;
244        default:
245            sLog.outError("Pet have incorrect type (%u) for pet loading.",getPetType());
246    }
247    InitStatsForLevel( petlevel);
248    SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL));
249    SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, fields[5].GetUInt32());
250    SetUInt64Value(UNIT_FIELD_CREATEDBY, owner->GetGUID());
251
252    m_charmInfo->SetReactState( ReactStates( fields[6].GetUInt8() ));
253    m_loyaltyPoints = fields[7].GetInt32();
254
255    uint32 savedhealth = fields[13].GetUInt32();
256    uint32 savedmana = fields[14].GetUInt32();
257
258    // set current pet as current
259    if(fields[10].GetUInt32() != 0)
260    {
261        CharacterDatabase.BeginTransaction();
262        CharacterDatabase.PExecute("UPDATE character_pet SET slot = '3' WHERE owner = '%u' AND slot = '0' AND id <> '%u'",ownerid, m_charmInfo->GetPetNumber());
263        CharacterDatabase.PExecute("UPDATE character_pet SET slot = '0' WHERE owner = '%u' AND id = '%u'",ownerid, m_charmInfo->GetPetNumber());
264        CharacterDatabase.CommitTransaction();
265    }
266
267    if(!is_temporary_summoned)
268    {
269        // permanent controlled pets store state in DB
270        Tokens tokens = StrSplit(fields[16].GetString(), " ");
271
272        if(tokens.size() != 20)
273        {
274            delete result;
275            return false;
276        }
277
278        int index;
279        Tokens::iterator iter;
280        for(iter = tokens.begin(), index = 0; index < 10; ++iter, ++index )
281        {
282            m_charmInfo->GetActionBarEntry(index)->Type = atol((*iter).c_str());
283            ++iter;
284            m_charmInfo->GetActionBarEntry(index)->SpellOrAction = atol((*iter).c_str());
285        }
286
287        //init teach spells
288        tokens = StrSplit(fields[17].GetString(), " ");
289        for (iter = tokens.begin(), index = 0; index < 4; ++iter, ++index)
290        {
291            uint32 tmp = atol((*iter).c_str());
292
293            ++iter;
294
295            if(tmp)
296                AddTeachSpell(tmp, atol((*iter).c_str()));
297            else
298                break;
299        }
300    }
301
302    // since last save (in seconds)
303    uint32 timediff = (time(NULL) - fields[18].GetUInt32());
304
305    delete result;
306
307    //load spells/cooldowns/auras
308    SetCanModifyStats(true);
309    _LoadAuras(timediff);
310
311    //init AB
312    if(is_temporary_summoned)
313    {
314        // Temporary summoned pets always have initial spell list at load
315        InitPetCreateSpells();
316    }
317    else
318    {
319        LearnPetPassives();
320        CastPetAuras(current);
321    }
322
323    if(getPetType() == SUMMON_PET && !current)              //all (?) summon pets come with full health when called, but not when they are current
324    {
325        SetHealth(GetMaxHealth());
326        SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
327    }
328    else
329    {
330        SetHealth(savedhealth > GetMaxHealth() ? GetMaxHealth() : savedhealth);
331        SetPower(POWER_MANA, savedmana > GetMaxPower(POWER_MANA) ? GetMaxPower(POWER_MANA) : savedmana);
332    }
333
334    AIM_Initialize();
335    map->Add((Creature*)this);
336
337    // Spells should be loaded after pet is added to map, because in CanCast is check on it
338    _LoadSpells();
339    _LoadSpellCooldowns();
340
341    owner->SetPet(this);                                    // in DB stored only full controlled creature
342    sLog.outDebug("New Pet has guid %u", GetGUIDLow());
343
344    if(owner->GetTypeId() == TYPEID_PLAYER)
345    {
346        ((Player*)owner)->PetSpellInitialize();
347        if(((Player*)owner)->GetGroup())
348            ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_PET);
349    }
350
351    if(owner->GetTypeId() == TYPEID_PLAYER && getPetType() == HUNTER_PET)
352    {
353        result = CharacterDatabase.PQuery("SELECT genitive, dative, accusative, instrumental, prepositional FROM character_pet_declinedname WHERE owner = '%u' AND id = '%u'",owner->GetGUIDLow(),GetCharmInfo()->GetPetNumber());
354
355        if(result)
356        {
357            if(m_declinedname)
358                delete m_declinedname;
359
360            m_declinedname = new DeclinedName;
361            Field *fields = result->Fetch();
362            for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
363            {
364                m_declinedname->name[i] = fields[i].GetCppString();
365            }
366        }
367    }
368
369    return true;
370}
371
372void Pet::SavePetToDB(PetSaveMode mode)
373{
374    if(!GetEntry())
375        return;
376
377    // save only fully controlled creature
378    if(!isControlled())
379        return;
380
381    uint32 curhealth = GetHealth();
382    uint32 curmana = GetPower(POWER_MANA);
383
384    switch(mode)
385    {
386        case PET_SAVE_IN_STABLE_SLOT_1:
387        case PET_SAVE_IN_STABLE_SLOT_2:
388        case PET_SAVE_NOT_IN_SLOT:
389        {
390            RemoveAllAuras();
391
392            //only alive hunter pets get auras saved, the others don't
393            if(!(getPetType() == HUNTER_PET && isAlive()))
394                m_Auras.clear();
395        }
396        default:
397            break;
398    }
399
400    _SaveSpells();
401    _SaveSpellCooldowns();
402    _SaveAuras();
403
404    switch(mode)
405    {
406        case PET_SAVE_AS_CURRENT:
407        case PET_SAVE_IN_STABLE_SLOT_1:
408        case PET_SAVE_IN_STABLE_SLOT_2:
409        case PET_SAVE_NOT_IN_SLOT:
410        {
411            uint32 loyalty =1;
412            if(getPetType()!=HUNTER_PET)
413                loyalty = GetLoyaltyLevel();
414
415            uint32 owner = GUID_LOPART(GetOwnerGUID());
416            std::string name = m_name;
417            CharacterDatabase.escape_string(name);
418            CharacterDatabase.BeginTransaction();
419            // remove current data
420            CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND id = '%u'", owner,m_charmInfo->GetPetNumber() );
421
422            // prevent duplicate using slot (except PET_SAVE_NOT_IN_SLOT)
423            if(mode!=PET_SAVE_NOT_IN_SLOT)
424                CharacterDatabase.PExecute("UPDATE character_pet SET slot = 3 WHERE owner = '%u' AND slot = '%u'", owner, uint32(mode) );
425
426            // prevent existence another hunter pet in PET_SAVE_AS_CURRENT and PET_SAVE_NOT_IN_SLOT
427            if(getPetType()==HUNTER_PET && (mode==PET_SAVE_AS_CURRENT||mode==PET_SAVE_NOT_IN_SLOT))
428                CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND (slot = '0' OR slot = '3')", owner );
429            // save pet
430            std::ostringstream ss;
431            ss  << "INSERT INTO character_pet ( id, entry,  owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata,TeachSpelldata,savetime,resettalents_cost,resettalents_time,CreatedBySpell,PetType) "
432                << "VALUES ("
433                << m_charmInfo->GetPetNumber() << ", "
434                << GetEntry() << ", "
435                << owner << ", "
436                << GetNativeDisplayId() << ", "
437                << getLevel() << ", "
438                << GetUInt32Value(UNIT_FIELD_PETEXPERIENCE) << ", "
439                << uint32(m_charmInfo->GetReactState()) << ", "
440                << m_loyaltyPoints << ", "
441                << GetLoyaltyLevel() << ", "
442                << m_TrainingPoints << ", "
443                << uint32(mode) << ", '"
444                << name.c_str() << "', "
445                << uint32((GetByteValue(UNIT_FIELD_BYTES_2, 2) == UNIT_RENAME_ALLOWED)?0:1) << ", "
446                << (curhealth<1?1:curhealth) << ", "
447                << curmana << ", "
448                << GetPower(POWER_HAPPINESS) << ", '";
449
450            for(uint32 i = 0; i < 10; i++)
451                ss << uint32(m_charmInfo->GetActionBarEntry(i)->Type) << " " << uint32(m_charmInfo->GetActionBarEntry(i)->SpellOrAction) << " ";
452            ss << "', '";
453
454            //save spells the pet can teach to it's Master
455            {
456                int i = 0;
457                for(TeachSpellMap::iterator itr = m_teachspells.begin(); i < 4 && itr != m_teachspells.end(); ++i, ++itr)
458                    ss << itr->first << " " << itr->second << " ";
459                for(; i < 4; ++i)
460                    ss << uint32(0) << " " << uint32(0) << " ";
461            }
462
463            ss  << "', "
464                << time(NULL) << ", "
465                << uint32(m_resetTalentsCost) << ", "
466                << uint64(m_resetTalentsTime) << ", "
467                << GetUInt32Value(UNIT_CREATED_BY_SPELL) << ", "
468                << uint32(getPetType()) << ")";
469
470            CharacterDatabase.Execute( ss.str().c_str() );
471
472            CharacterDatabase.CommitTransaction();
473            break;
474        }
475        case PET_SAVE_AS_DELETED:
476        {
477            RemoveAllAuras();
478            uint32 owner = GUID_LOPART(GetOwnerGUID());
479            DeleteFromDB(m_charmInfo->GetPetNumber());
480            break;
481        }
482        default:
483            sLog.outError("Unknown pet save/remove mode: %d",mode);
484    }
485}
486
487void Pet::DeleteFromDB(uint32 guidlow)
488{
489    CharacterDatabase.PExecute("DELETE FROM character_pet WHERE id = '%u'", guidlow);
490    CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE id = '%u'", guidlow);
491    CharacterDatabase.PExecute("DELETE FROM pet_aura WHERE guid = '%u'", guidlow);
492    CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE guid = '%u'", guidlow);
493    CharacterDatabase.PExecute("DELETE FROM pet_spell_cooldown WHERE guid = '%u'", guidlow);
494}
495
496void Pet::setDeathState(DeathState s)                       // overwrite virtual Creature::setDeathState and Unit::setDeathState
497{
498    Creature::setDeathState(s);
499    if(getDeathState()==CORPSE)
500    {
501        //remove summoned pet (no corpse)
502        if(getPetType()==SUMMON_PET)
503            Remove(PET_SAVE_NOT_IN_SLOT);
504        // other will despawn at corpse desppawning (Pet::Update code)
505        else
506        {
507            // pet corpse non lootable and non skinnable
508            SetUInt32Value( UNIT_DYNAMIC_FLAGS, 0x00 );
509            RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
510
511             //lose happiness when died and not in BG/Arena
512            MapEntry const* mapEntry = sMapStore.LookupEntry(GetMapId());
513            if(!mapEntry || (mapEntry->map_type != MAP_ARENA && mapEntry->map_type != MAP_BATTLEGROUND))
514                ModifyPower(POWER_HAPPINESS, -HAPPINESS_LEVEL_SIZE);
515
516            SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE);
517        }
518    }
519    else if(getDeathState()==ALIVE)
520    {
521        RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE);
522        CastPetAuras(true);
523    }
524}
525
526void Pet::Update(uint32 diff)
527{
528    if(m_removed)                                           // pet already removed, just wait in remove queue, no updates
529        return;
530
531    switch( m_deathState )
532    {
533        case CORPSE:
534        {
535            if( m_deathTimer <= diff )
536            {
537                assert(getPetType()!=SUMMON_PET && "Must be already removed.");
538                Remove(PET_SAVE_NOT_IN_SLOT);               //hunters' pets never get removed because of death, NEVER!
539                return;
540            }
541            break;
542        }
543        case ALIVE:
544        {
545            // unsummon pet that lost owner
546            Unit* owner = GetOwner();
547            if(!owner || !IsWithinDistInMap(owner, OWNER_MAX_DISTANCE) || isControlled() && !owner->GetPetGUID())
548            {
549                Remove(PET_SAVE_NOT_IN_SLOT, true);
550                return;
551            }
552
553            if(isControlled())
554            {
555                if( owner->GetPetGUID() != GetGUID() )
556                {
557                    Remove(getPetType()==HUNTER_PET?PET_SAVE_AS_DELETED:PET_SAVE_NOT_IN_SLOT);
558                    return;
559                }
560            }
561
562            if(m_duration > 0)
563            {
564                if(m_duration > diff)
565                    m_duration -= diff;
566                else
567                {
568                    Remove(getPetType() != SUMMON_PET ? PET_SAVE_AS_DELETED:PET_SAVE_NOT_IN_SLOT);
569                    return;
570                }
571            }
572
573            if(getPetType() != HUNTER_PET)
574                break;
575
576            //regenerate Focus
577            if(m_regenTimer <= diff)
578            {
579                RegenerateFocus();
580                m_regenTimer = 4000;
581            }
582            else
583                m_regenTimer -= diff;
584
585            if(m_happinessTimer <= diff)
586            {
587                LooseHappiness();
588                m_happinessTimer = 7500;
589            }
590            else
591                m_happinessTimer -= diff;
592
593            if(m_loyaltyTimer <= diff)
594            {
595                TickLoyaltyChange();
596                m_loyaltyTimer = 12000;
597            }
598            else
599                m_loyaltyTimer -= diff;
600
601            break;
602        }
603        default:
604            break;
605    }
606    Creature::Update(diff);
607}
608
609void Pet::RegenerateFocus()
610{
611    uint32 curValue = GetPower(POWER_FOCUS);
612    uint32 maxValue = GetMaxPower(POWER_FOCUS);
613
614    if (curValue >= maxValue)
615        return;
616
617    float addvalue = 24 * sWorld.getRate(RATE_POWER_FOCUS);
618
619    AuraList const& ModPowerRegenPCTAuras = GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT);
620    for(AuraList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i)
621        if ((*i)->GetModifier()->m_miscvalue == POWER_FOCUS)
622            addvalue *= ((*i)->GetModifier()->m_amount + 100) / 100.0f;
623
624    ModifyPower(POWER_FOCUS, (int32)addvalue);
625}
626
627void Pet::LooseHappiness()
628{
629    uint32 curValue = GetPower(POWER_HAPPINESS);
630    if (curValue <= 0)
631        return;
632    int32 addvalue = (140 >> GetLoyaltyLevel()) * 125;      //value is 70/35/17/8/4 (per min) * 1000 / 8 (timer 7.5 secs)
633    if(isInCombat())                                        //we know in combat happiness fades faster, multiplier guess
634        addvalue = int32(addvalue * 1.5);
635    ModifyPower(POWER_HAPPINESS, -addvalue);
636}
637
638void Pet::ModifyLoyalty(int32 addvalue)
639{
640    uint32 loyaltylevel = GetLoyaltyLevel();
641
642    if(addvalue > 0)                                        //only gain influenced, not loss
643        addvalue = int32((float)addvalue * sWorld.getRate(RATE_LOYALTY));
644
645    if(loyaltylevel >= BEST_FRIEND && (addvalue + m_loyaltyPoints) > int32(GetMaxLoyaltyPoints(loyaltylevel)))
646        return;
647
648    m_loyaltyPoints += addvalue;
649
650    if(m_loyaltyPoints < 0)
651    {
652        if(loyaltylevel > REBELLIOUS)
653        {
654            //level down
655            --loyaltylevel;
656            SetLoyaltyLevel(LoyaltyLevel(loyaltylevel));
657            m_loyaltyPoints = GetStartLoyaltyPoints(loyaltylevel);
658            SetTP(m_TrainingPoints - int32(getLevel()));
659        }
660        else
661        {
662            m_loyaltyPoints = 0;
663            Unit* owner = GetOwner();
664            if(owner && owner->GetTypeId() == TYPEID_PLAYER)
665            {
666                WorldPacket data(SMSG_PET_BROKEN, 0);
667                ((Player*)owner)->GetSession()->SendPacket(&data);
668
669                //run away
670                ((Player*)owner)->RemovePet(this,PET_SAVE_AS_DELETED);
671            }
672        }
673    }
674    //level up
675    else if(m_loyaltyPoints > int32(GetMaxLoyaltyPoints(loyaltylevel)))
676    {
677        ++loyaltylevel;
678        SetLoyaltyLevel(LoyaltyLevel(loyaltylevel));
679        m_loyaltyPoints = GetStartLoyaltyPoints(loyaltylevel);
680        SetTP(m_TrainingPoints + getLevel());
681    }
682}
683
684void Pet::TickLoyaltyChange()
685{
686    int32 addvalue;
687
688    switch(GetHappinessState())
689    {
690        case HAPPY:   addvalue =  20; break;
691        case CONTENT: addvalue =  10; break;
692        case UNHAPPY: addvalue = -20; break;
693        default:
694            return;
695    }
696    ModifyLoyalty(addvalue);
697}
698
699void Pet::KillLoyaltyBonus(uint32 level)
700{
701    if(level > 100)
702        return;
703
704                                                            //at lower levels gain is faster | the lower loyalty the more loyalty is gained
705    uint32 bonus = uint32(((100 - level) / 10) + (6 - GetLoyaltyLevel()));
706    ModifyLoyalty(bonus);
707}
708
709HappinessState Pet::GetHappinessState()
710{
711    if(GetPower(POWER_HAPPINESS) < HAPPINESS_LEVEL_SIZE)
712        return UNHAPPY;
713    else if(GetPower(POWER_HAPPINESS) >= HAPPINESS_LEVEL_SIZE * 2)
714        return HAPPY;
715    else
716        return CONTENT;
717}
718
719void Pet::SetLoyaltyLevel(LoyaltyLevel level)
720{
721    SetByteValue(UNIT_FIELD_BYTES_1, 1, level);
722}
723
724bool Pet::CanTakeMoreActiveSpells(uint32 spellid)
725{
726    uint8  activecount = 1;
727    uint32 chainstartstore[ACTIVE_SPELLS_MAX];
728
729    if(IsPassiveSpell(spellid))
730        return true;
731
732    chainstartstore[0] = spellmgr.GetFirstSpellInChain(spellid);
733
734    for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
735    {
736        if(IsPassiveSpell(itr->first))
737            continue;
738
739        uint32 chainstart = spellmgr.GetFirstSpellInChain(itr->first);
740
741        uint8 x;
742
743        for(x = 0; x < activecount; x++)
744        {
745            if(chainstart == chainstartstore[x])
746                break;
747        }
748
749        if(x == activecount)                                //spellchain not yet saved -> add active count
750        {
751            ++activecount;
752            if(activecount > ACTIVE_SPELLS_MAX)
753                return false;
754            chainstartstore[x] = chainstart;
755        }
756    }
757    return true;
758}
759
760bool Pet::HasTPForSpell(uint32 spellid)
761{
762    int32 neededtrainp = GetTPForSpell(spellid);
763    if((m_TrainingPoints - neededtrainp < 0 || neededtrainp < 0) && neededtrainp != 0)
764        return false;
765    return true;
766}
767
768int32 Pet::GetTPForSpell(uint32 spellid)
769{
770    uint32 basetrainp = 0;
771
772    SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spellid);
773    SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spellid);
774    for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
775    {
776        if(!_spell_idx->second->reqtrainpoints)
777            return 0;
778
779        basetrainp = _spell_idx->second->reqtrainpoints;
780        break;
781    }
782
783    uint32 spenttrainp = 0;
784    uint32 chainstart = spellmgr.GetFirstSpellInChain(spellid);
785
786    for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
787    {
788        if(itr->second->state == PETSPELL_REMOVED)
789            continue;
790
791        if(spellmgr.GetFirstSpellInChain(itr->first) == chainstart)
792        {
793            SkillLineAbilityMap::const_iterator _lower = spellmgr.GetBeginSkillLineAbilityMap(itr->first);
794            SkillLineAbilityMap::const_iterator _upper = spellmgr.GetEndSkillLineAbilityMap(itr->first);
795
796            for(SkillLineAbilityMap::const_iterator _spell_idx2 = _lower; _spell_idx2 != _upper; ++_spell_idx2)
797            {
798                if(_spell_idx2->second->reqtrainpoints > spenttrainp)
799                {
800                    spenttrainp = _spell_idx2->second->reqtrainpoints;
801                    break;
802                }
803            }
804        }
805    }
806
807    return int32(basetrainp) - int32(spenttrainp);
808}
809
810uint32 Pet::GetMaxLoyaltyPoints(uint32 level)
811{
812    return LevelUpLoyalty[level - 1];
813}
814
815uint32 Pet::GetStartLoyaltyPoints(uint32 level)
816{
817    return LevelStartLoyalty[level - 1];
818}
819
820void Pet::SetTP(int32 TP)
821{
822    m_TrainingPoints = TP;
823    SetUInt32Value(UNIT_TRAINING_POINTS, (uint32)GetDispTP());
824}
825
826int32 Pet::GetDispTP()
827{
828    if(getPetType()!= HUNTER_PET)
829        return(0);
830    if(m_TrainingPoints < 0)
831        return -m_TrainingPoints;
832    else
833        return -(m_TrainingPoints + 1);
834}
835
836void Pet::Remove(PetSaveMode mode, bool returnreagent)
837{
838    Unit* owner = GetOwner();
839
840    if(owner)
841    {
842        if(owner->GetTypeId()==TYPEID_PLAYER)
843        {
844            ((Player*)owner)->RemovePet(this,mode,returnreagent);
845            return;
846        }
847
848        // only if current pet in slot
849        if(owner->GetPetGUID()==GetGUID())
850            owner->SetPet(0);
851    }
852
853    CleanupsBeforeDelete();
854    AddObjectToRemoveList();
855    m_removed = true;
856}
857
858void Pet::GivePetXP(uint32 xp)
859{
860    if(getPetType() != HUNTER_PET)
861        return;
862
863    if ( xp < 1 )
864        return;
865
866    if(!isAlive())
867        return;
868
869    uint32 level = getLevel();
870
871    // XP to money conversion processed in Player::RewardQuest
872    if(level >= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
873        return;
874
875    uint32 curXP = GetUInt32Value(UNIT_FIELD_PETEXPERIENCE);
876    uint32 nextLvlXP = GetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP);
877    uint32 newXP = curXP + xp;
878
879    if(newXP >= nextLvlXP && level+1 > GetOwner()->getLevel())
880    {
881        SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, nextLvlXP-1);
882        return;
883    }
884
885    while( newXP >= nextLvlXP && level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) )
886    {
887        newXP -= nextLvlXP;
888
889        SetLevel( level + 1 );
890        SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((MaNGOS::XP::xp_to_level(level+1))/4));
891
892        level = getLevel();
893        nextLvlXP = GetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP);
894        GivePetLevel(level);
895    }
896
897    SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, newXP);
898
899    if(getPetType() == HUNTER_PET)
900        KillLoyaltyBonus(level);
901}
902
903void Pet::GivePetLevel(uint32 level)
904{
905    if(!level)
906        return;
907
908    InitStatsForLevel( level);
909
910    SetTP(m_TrainingPoints + (GetLoyaltyLevel() - 1));
911}
912
913bool Pet::CreateBaseAtCreature(Creature* creature)
914{
915    if(!creature)
916    {
917        sLog.outError("CRITICAL ERROR: NULL pointer parsed into CreateBaseAtCreature()");
918        return false;
919    }
920    uint32 guid=objmgr.GenerateLowGuid(HIGHGUID_PET);
921
922    sLog.outBasic("SetInstanceID()");
923    SetInstanceId(creature->GetInstanceId());
924
925    sLog.outBasic("Create pet");
926    uint32 pet_number = objmgr.GeneratePetNumber();
927    if(!Create(guid, creature->GetMap(), creature->GetEntry(), pet_number))
928        return false;
929
930    Relocate(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ(), creature->GetOrientation());
931
932    if(!IsPositionValid())
933    {
934        sLog.outError("ERROR: Pet (guidlow %d, entry %d) not created base at creature. Suggested coordinates isn't valid (X: %d Y: ^%d)", GetGUIDLow(), GetEntry(), GetPositionX(), GetPositionY());
935        return false;
936    }
937
938    CreatureInfo const *cinfo = GetCreatureInfo();
939    if(!cinfo)
940    {
941        sLog.outError("ERROR: CreateBaseAtCreature() failed, creatureInfo is missing!");
942        return false;
943    }
944
945    if(cinfo->type == CREATURE_TYPE_CRITTER)
946    {
947        setPetType(MINI_PET);
948        return true;
949    }
950    SetDisplayId(creature->GetDisplayId());
951    SetNativeDisplayId(creature->GetNativeDisplayId());
952    SetMaxPower(POWER_HAPPINESS,GetCreatePowers(POWER_HAPPINESS));
953    SetPower(   POWER_HAPPINESS,166500);
954    setPowerType(POWER_FOCUS);
955    SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP,0);
956    SetUInt32Value(UNIT_FIELD_PETEXPERIENCE,0);
957    SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((MaNGOS::XP::xp_to_level(creature->getLevel()))/4));
958    SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
959    SetUInt32Value(UNIT_NPC_FLAGS , 0);
960
961    CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(creature->GetCreatureInfo()->family);
962    if( char* familyname = cFamily->Name[sWorld.GetDefaultDbcLocale()] )
963        SetName(familyname);
964    else
965        SetName(creature->GetName());
966
967    m_loyaltyPoints = 1000;
968    if(cinfo->type == CREATURE_TYPE_BEAST)
969    {
970        SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100);
971        SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE );
972        SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 );
973        SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_ALLOWED);
974
975        SetUInt32Value(UNIT_MOD_CAST_SPEED, creature->GetUInt32Value(UNIT_MOD_CAST_SPEED) );
976        SetLoyaltyLevel(REBELLIOUS);
977    }
978    return true;
979}
980
981bool Pet::InitStatsForLevel(uint32 petlevel)
982{
983    CreatureInfo const *cinfo = GetCreatureInfo();
984    assert(cinfo);
985
986    Unit* owner = GetOwner();
987    if(!owner)
988    {
989        sLog.outError("ERROR: attempt to summon pet (Entry %u) without owner! Attempt terminated.", cinfo->Entry);
990        return false;
991    }
992
993    uint32 creature_ID = (getPetType() == HUNTER_PET) ? 1 : cinfo->Entry;
994
995    SetLevel( petlevel);
996
997    SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool));
998
999    SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(petlevel*50));
1000
1001    SetAttackTime(BASE_ATTACK, BASE_ATTACK_TIME);
1002    SetAttackTime(OFF_ATTACK, BASE_ATTACK_TIME);
1003    SetAttackTime(RANGED_ATTACK, BASE_ATTACK_TIME);
1004
1005    SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0);
1006
1007    CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cinfo->family);
1008    if(cFamily && cFamily->minScale > 0.0f)
1009    {
1010        float scale;
1011        if (getLevel() >= cFamily->maxScaleLevel)
1012            scale = cFamily->maxScale;
1013        else if (getLevel() <= cFamily->minScaleLevel)
1014            scale = cFamily->minScale;
1015        else
1016            scale = cFamily->minScale + (getLevel() - cFamily->minScaleLevel) / cFamily->maxScaleLevel * (cFamily->maxScale - cFamily->minScale);
1017
1018        SetFloatValue(OBJECT_FIELD_SCALE_X, scale);
1019    }
1020    m_bonusdamage = 0;
1021
1022    int32 createResistance[MAX_SPELL_SCHOOL] = {0,0,0,0,0,0,0};
1023
1024    if(cinfo && getPetType() != HUNTER_PET)
1025    {
1026        createResistance[SPELL_SCHOOL_HOLY]   = cinfo->resistance1;
1027        createResistance[SPELL_SCHOOL_FIRE]   = cinfo->resistance2;
1028        createResistance[SPELL_SCHOOL_NATURE] = cinfo->resistance3;
1029        createResistance[SPELL_SCHOOL_FROST]  = cinfo->resistance4;
1030        createResistance[SPELL_SCHOOL_SHADOW] = cinfo->resistance5;
1031        createResistance[SPELL_SCHOOL_ARCANE] = cinfo->resistance6;
1032    }
1033
1034    switch(getPetType())
1035    {
1036        case SUMMON_PET:
1037        {
1038            if(owner->GetTypeId() == TYPEID_PLAYER)
1039            {
1040                switch(owner->getClass())
1041                {
1042                    case CLASS_WARLOCK:
1043                    {
1044
1045                        //the damage bonus used for pets is either fire or shadow damage, whatever is higher
1046                        uint32 fire  = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FIRE);
1047                        uint32 shadow = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_SHADOW);
1048                        uint32 val  = (fire > shadow) ? fire : shadow;
1049
1050                        SetBonusDamage(int32 (val * 0.15f));
1051                        //bonusAP += val * 0.57;
1052                        break;
1053                    }
1054                    case CLASS_MAGE:
1055                    {
1056                                                            //40% damage bonus of mage's frost damage
1057                        float val = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FROST) * 0.4;
1058                        if(val < 0)
1059                            val = 0;
1060                        SetBonusDamage( int32(val));
1061                        break;
1062                    }
1063                    default:
1064                        break;
1065                }
1066            }
1067
1068            SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4)) );
1069            SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4)) );
1070
1071            //SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, float(cinfo->attackpower));
1072
1073            PetLevelInfo const* pInfo = objmgr.GetPetLevelInfo(creature_ID, petlevel);
1074            if(pInfo)                                       // exist in DB
1075            {
1076                SetCreateHealth(pInfo->health);
1077                SetCreateMana(pInfo->mana);
1078
1079                if(pInfo->armor > 0)
1080                    SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor));
1081
1082                for(int stat = 0; stat < MAX_STATS; ++stat)
1083                {
1084                    SetCreateStat(Stats(stat),float(pInfo->stats[stat]));
1085                }
1086            }
1087            else                                            // not exist in DB, use some default fake data
1088            {
1089                sLog.outErrorDb("Summoned pet (Entry: %u) not have pet stats data in DB",cinfo->Entry);
1090
1091                // remove elite bonuses included in DB values
1092                SetCreateHealth(uint32(((float(cinfo->maxhealth) / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) );
1093                SetCreateMana(  uint32(((float(cinfo->maxmana)   / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) );
1094
1095                SetCreateStat(STAT_STRENGTH,22);
1096                SetCreateStat(STAT_AGILITY,22);
1097                SetCreateStat(STAT_STAMINA,25);
1098                SetCreateStat(STAT_INTELLECT,28);
1099                SetCreateStat(STAT_SPIRIT,27);
1100            }
1101            break;
1102        }
1103        case HUNTER_PET:
1104        {
1105            SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((MaNGOS::XP::xp_to_level(petlevel))/4));
1106
1107            //these formula may not be correct; however, it is designed to be close to what it should be
1108            //this makes dps 0.5 of pets level
1109            SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4)) );
1110            //damage range is then petlevel / 2
1111            SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4)) );
1112            //damage is increased afterwards as strength and pet scaling modify attack power
1113
1114            //stored standard pet stats are entry 1 in pet_levelinfo
1115            PetLevelInfo const* pInfo = objmgr.GetPetLevelInfo(creature_ID, petlevel);
1116            if(pInfo)                                       // exist in DB
1117            {
1118                SetCreateHealth(pInfo->health);
1119                SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor));
1120                //SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, float(cinfo->attackpower));
1121
1122                for( int i = STAT_STRENGTH; i < MAX_STATS; i++)
1123                {
1124                    SetCreateStat(Stats(i),  float(pInfo->stats[i]));
1125                }
1126            }
1127            else                                            // not exist in DB, use some default fake data
1128            {
1129                sLog.outErrorDb("Hunter pet levelstats missing in DB");
1130
1131                // remove elite bonuses included in DB values
1132                SetCreateHealth( uint32(((float(cinfo->maxhealth) / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) );
1133
1134                SetCreateStat(STAT_STRENGTH,22);
1135                SetCreateStat(STAT_AGILITY,22);
1136                SetCreateStat(STAT_STAMINA,25);
1137                SetCreateStat(STAT_INTELLECT,28);
1138                SetCreateStat(STAT_SPIRIT,27);
1139            }
1140            break;
1141        }
1142        case GUARDIAN_PET:
1143            SetUInt32Value(UNIT_FIELD_PETEXPERIENCE,0);
1144            SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP,1000);
1145
1146            SetCreateMana(   28 + 10*petlevel );
1147            SetCreateHealth( 28 + 30*petlevel );
1148
1149            // FIXME: this is wrong formula, possible each guardian pet have own damage formula
1150            //these formula may not be correct; however, it is designed to be close to what it should be
1151            //this makes dps 0.5 of pets level
1152            SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4)) );
1153            //damage range is then petlevel / 2
1154            SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4)) );
1155            break;
1156        default:
1157            sLog.outError("Pet have incorrect type (%u) for levelup.",getPetType());            break;
1158    }
1159
1160    for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
1161        SetModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START + i), BASE_VALUE, float(createResistance[i]) );
1162
1163    UpdateAllStats();
1164
1165    SetHealth(GetMaxHealth());
1166    SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
1167
1168    return true;
1169}
1170
1171bool Pet::HaveInDiet(ItemPrototype const* item) const
1172{
1173    if (!item->FoodType)
1174        return false;
1175
1176    CreatureInfo const* cInfo = GetCreatureInfo();
1177    if(!cInfo)
1178        return false;
1179
1180    CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cInfo->family);
1181    if(!cFamily)
1182        return false;
1183
1184    uint32 diet = cFamily->petFoodMask;
1185    uint32 FoodMask = 1 << (item->FoodType-1);
1186    return diet & FoodMask;
1187}
1188
1189uint32 Pet::GetCurrentFoodBenefitLevel(uint32 itemlevel)
1190{
1191    // -5 or greater food level
1192    if(getLevel() <= itemlevel +5)                          //possible to feed level 60 pet with level 55 level food for full effect
1193        return 35000;
1194    // -10..-6
1195    else if(getLevel() <= itemlevel + 10)                   //pure guess, but sounds good
1196        return 17000;
1197    // -14..-11
1198    else if(getLevel() <= itemlevel + 14)                   //level 55 food gets green on 70, makes sense to me
1199        return 8000;
1200    // -15 or less
1201    else
1202        return 0;                                           //food too low level
1203}
1204
1205void Pet::_LoadSpellCooldowns()
1206{
1207    m_CreatureSpellCooldowns.clear();
1208    m_CreatureCategoryCooldowns.clear();
1209
1210    QueryResult *result = CharacterDatabase.PQuery("SELECT spell,time FROM pet_spell_cooldown WHERE guid = '%u'",m_charmInfo->GetPetNumber());
1211
1212    if(result)
1213    {
1214        time_t curTime = time(NULL);
1215
1216        WorldPacket data(SMSG_SPELL_COOLDOWN, (8+1+result->GetRowCount()*8));
1217        data << GetGUID();
1218        data << uint8(0x0);
1219
1220        do
1221        {
1222            Field *fields = result->Fetch();
1223
1224            uint32 spell_id = fields[0].GetUInt32();
1225            time_t db_time  = (time_t)fields[1].GetUInt64();
1226
1227            if(!sSpellStore.LookupEntry(spell_id))
1228            {
1229                sLog.outError("Pet %u have unknown spell %u in `pet_spell_cooldown`, skipping.",m_charmInfo->GetPetNumber(),spell_id);
1230                continue;
1231            }
1232
1233            // skip outdated cooldown
1234            if(db_time <= curTime)
1235                continue;
1236
1237            data << uint32(spell_id);
1238            data << uint32(uint32(db_time-curTime)*1000);   // in m.secs
1239
1240            _AddCreatureSpellCooldown(spell_id,db_time);
1241
1242            sLog.outDebug("Pet (Number: %u) spell %u cooldown loaded (%u secs).",m_charmInfo->GetPetNumber(),spell_id,uint32(db_time-curTime));
1243        }
1244        while( result->NextRow() );
1245
1246        delete result;
1247
1248        if(!m_CreatureSpellCooldowns.empty() && GetOwner())
1249        {
1250            ((Player*)GetOwner())->GetSession()->SendPacket(&data);
1251        }
1252    }
1253}
1254
1255void Pet::_SaveSpellCooldowns()
1256{
1257    CharacterDatabase.PExecute("DELETE FROM pet_spell_cooldown WHERE guid = '%u'", m_charmInfo->GetPetNumber());
1258
1259    time_t curTime = time(NULL);
1260
1261    // remove oudated and save active
1262    for(CreatureSpellCooldowns::iterator itr = m_CreatureSpellCooldowns.begin();itr != m_CreatureSpellCooldowns.end();)
1263    {
1264        if(itr->second <= curTime)
1265            m_CreatureSpellCooldowns.erase(itr++);
1266        else
1267        {
1268            CharacterDatabase.PExecute("INSERT INTO pet_spell_cooldown (guid,spell,time) VALUES ('%u', '%u', '" I64FMTD "')", m_charmInfo->GetPetNumber(), itr->first, uint64(itr->second));
1269            ++itr;
1270        }
1271    }
1272}
1273
1274void Pet::_LoadSpells()
1275{
1276    QueryResult *result = CharacterDatabase.PQuery("SELECT spell,slot,active FROM pet_spell WHERE guid = '%u'",m_charmInfo->GetPetNumber());
1277
1278    if(result)
1279    {
1280        do
1281        {
1282            Field *fields = result->Fetch();
1283
1284            addSpell(fields[0].GetUInt16(), fields[2].GetUInt16(), PETSPELL_UNCHANGED, fields[1].GetUInt16());
1285        }
1286        while( result->NextRow() );
1287
1288        delete result;
1289    }
1290}
1291
1292void Pet::_SaveSpells()
1293{
1294    for (PetSpellMap::const_iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end(); itr = next)
1295    {
1296        ++next;
1297        if (itr->second->type == PETSPELL_FAMILY) continue; // prevent saving family passives to DB
1298        if (itr->second->state == PETSPELL_REMOVED || itr->second->state == PETSPELL_CHANGED)
1299            CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE guid = '%u' and spell = '%u'", m_charmInfo->GetPetNumber(), itr->first);
1300        if (itr->second->state == PETSPELL_NEW || itr->second->state == PETSPELL_CHANGED)
1301            CharacterDatabase.PExecute("INSERT INTO pet_spell (guid,spell,slot,active) VALUES ('%u', '%u', '%u','%u')", m_charmInfo->GetPetNumber(), itr->first, itr->second->slotId,itr->second->active);
1302
1303        if (itr->second->state == PETSPELL_REMOVED)
1304            _removeSpell(itr->first);
1305        else
1306            itr->second->state = PETSPELL_UNCHANGED;
1307    }
1308}
1309
1310void Pet::_LoadAuras(uint32 timediff)
1311{
1312    m_Auras.clear();
1313    for (int i = 0; i < TOTAL_AURAS; i++)
1314        m_modAuras[i].clear();
1315
1316    // all aura related fields
1317    for(int i = UNIT_FIELD_AURA; i <= UNIT_FIELD_AURASTATE; ++i)
1318        SetUInt32Value(i, 0);
1319
1320    QueryResult *result = CharacterDatabase.PQuery("SELECT caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges FROM pet_aura WHERE guid = '%u'",m_charmInfo->GetPetNumber());
1321
1322    if(result)
1323    {
1324        do
1325        {
1326            Field *fields = result->Fetch();
1327            uint64 caster_guid = fields[0].GetUInt64();
1328            uint32 spellid = fields[1].GetUInt32();
1329            uint32 effindex = fields[2].GetUInt32();
1330            int32 damage     = (int32)fields[3].GetUInt32();
1331            int32 maxduration = (int32)fields[4].GetUInt32();
1332            int32 remaintime = (int32)fields[5].GetUInt32();
1333            int32 remaincharges = (int32)fields[6].GetUInt32();
1334
1335            SpellEntry const* spellproto = sSpellStore.LookupEntry(spellid);
1336            if(!spellproto)
1337            {
1338                sLog.outError("Unknown aura (spellid %u, effindex %u), ignore.",spellid,effindex);
1339                continue;
1340            }
1341
1342            if(effindex >= 3)
1343            {
1344                sLog.outError("Invalid effect index (spellid %u, effindex %u), ignore.",spellid,effindex);
1345                continue;
1346            }
1347
1348            // negative effects should continue counting down after logout
1349            if (remaintime != -1 && !IsPositiveEffect(spellid, effindex))
1350            {
1351                if(remaintime  <= int32(timediff))
1352                    continue;
1353
1354                remaintime -= timediff;
1355            }
1356
1357            // prevent wrong values of remaincharges
1358            if(spellproto->procCharges)
1359            {
1360                if(remaincharges <= 0 || remaincharges > spellproto->procCharges)
1361                    remaincharges = spellproto->procCharges;
1362            }
1363            else
1364                remaincharges = -1;
1365
1366            Aura* aura = CreateAura(spellproto, effindex, NULL, this, NULL);
1367
1368            if(!damage)
1369                damage = aura->GetModifier()->m_amount;
1370            aura->SetLoadedState(caster_guid,damage,maxduration,remaintime,remaincharges);
1371            AddAura(aura);
1372        }
1373        while( result->NextRow() );
1374
1375        delete result;
1376    }
1377}
1378
1379void Pet::_SaveAuras()
1380{
1381    CharacterDatabase.PExecute("DELETE FROM pet_aura WHERE guid = '%u'",m_charmInfo->GetPetNumber());
1382
1383    AuraMap const& auras = GetAuras();
1384    for(AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
1385    {
1386        // skip all auras from spell that apply at cast SPELL_AURA_MOD_SHAPESHIFT or pet area auras.
1387        SpellEntry const *spellInfo = itr->second->GetSpellProto();
1388        uint8 i;
1389        for (i = 0; i < 3; i++)
1390            if (spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_STEALTH ||
1391                spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_OWNER ||
1392                spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_PET )
1393                break;
1394
1395        if (i == 3 && !itr->second->IsPassive())
1396            CharacterDatabase.PExecute("INSERT INTO pet_aura (guid,caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges) "
1397                "VALUES ('%u', '" I64FMTD "', '%u', '%u', '%d', '%d', '%d', '%d')",
1398                m_charmInfo->GetPetNumber(), itr->second->GetCasterGUID(),(uint32)(*itr).second->GetId(), (uint32)(*itr).second->GetEffIndex(),(*itr).second->GetModifier()->m_amount,int((*itr).second->GetAuraMaxDuration()),int((*itr).second->GetAuraDuration()),int((*itr).second->m_procCharges));
1399    }
1400}
1401
1402bool Pet::addSpell(uint16 spell_id, uint16 active, PetSpellState state, uint16 slot_id, PetSpellType type)
1403{
1404    SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
1405    if (!spellInfo)
1406    {
1407        // do pet spell book cleanup
1408        if(state == PETSPELL_UNCHANGED)                     // spell load case
1409        {
1410            sLog.outError("Pet::addSpell: Non-existed in SpellStore spell #%u request, deleting for all pets in `pet_spell`.",spell_id);
1411            CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE spell = '%u'",spell_id);
1412        }
1413        else
1414            sLog.outError("Pet::addSpell: Non-existed in SpellStore spell #%u request.",spell_id);
1415
1416        return false;
1417    }
1418
1419    PetSpellMap::iterator itr = m_spells.find(spell_id);
1420    if (itr != m_spells.end())
1421    {
1422        if (itr->second->state == PETSPELL_REMOVED)
1423        {
1424            delete itr->second;
1425            m_spells.erase(itr);
1426            state = PETSPELL_CHANGED;
1427        }
1428        else if (state == PETSPELL_UNCHANGED && itr->second->state != PETSPELL_UNCHANGED)
1429        {
1430            // can be in case spell loading but learned at some previous spell loading
1431            itr->second->state = PETSPELL_UNCHANGED;
1432            return false;
1433        }
1434        else
1435            return false;
1436    }
1437
1438    uint32 oldspell_id = 0;
1439
1440    PetSpell *newspell = new PetSpell;
1441    newspell->state = state;
1442    newspell->type = type;
1443
1444    if(active == ACT_DECIDE)                                //active was not used before, so we save it's autocast/passive state here
1445    {
1446        if(IsPassiveSpell(spell_id))
1447            newspell->active = ACT_PASSIVE;
1448        else
1449            newspell->active = ACT_DISABLED;
1450    }
1451    else
1452        newspell->active = active;
1453
1454    uint32 chainstart = spellmgr.GetFirstSpellInChain(spell_id);
1455
1456    for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); itr++)
1457    {
1458        if(itr->second->state == PETSPELL_REMOVED) continue;
1459
1460        if(spellmgr.GetFirstSpellInChain(itr->first) == chainstart)
1461        {
1462            slot_id = itr->second->slotId;
1463            newspell->active = itr->second->active;
1464
1465            if(newspell->active == ACT_ENABLED)
1466                ToggleAutocast(itr->first, false);
1467
1468            oldspell_id = itr->first;
1469            removeSpell(itr->first);
1470        }
1471    }
1472
1473    uint16 tmpslot=slot_id;
1474
1475    if (tmpslot == 0xffff)
1476    {
1477        uint16 maxid = 0;
1478        PetSpellMap::iterator itr;
1479        for (itr = m_spells.begin(); itr != m_spells.end(); ++itr)
1480        {
1481            if(itr->second->state == PETSPELL_REMOVED) continue;
1482            if (itr->second->slotId > maxid) maxid = itr->second->slotId;
1483        }
1484        tmpslot = maxid + 1;
1485    }
1486
1487    newspell->slotId = tmpslot;
1488    m_spells[spell_id] = newspell;
1489
1490    if (IsPassiveSpell(spell_id))
1491        CastSpell(this, spell_id, true);
1492    else if(state == PETSPELL_NEW)
1493        m_charmInfo->AddSpellToAB(oldspell_id, spell_id);
1494
1495    if(newspell->active == ACT_ENABLED)
1496        ToggleAutocast(spell_id, true);
1497
1498    return true;
1499}
1500
1501bool Pet::learnSpell(uint16 spell_id)
1502{
1503    // prevent duplicated entires in spell book
1504    if (!addSpell(spell_id))
1505        return false;
1506
1507    Unit* owner = GetOwner();
1508    if(owner->GetTypeId()==TYPEID_PLAYER)
1509        ((Player*)owner)->PetSpellInitialize();
1510    return true;
1511}
1512
1513void Pet::removeSpell(uint16 spell_id)
1514{
1515    PetSpellMap::iterator itr = m_spells.find(spell_id);
1516    if (itr == m_spells.end())
1517        return;
1518
1519    if(itr->second->state == PETSPELL_REMOVED)
1520        return;
1521
1522    if(itr->second->state == PETSPELL_NEW)
1523    {
1524        delete itr->second;
1525        m_spells.erase(itr);
1526    }
1527    else
1528        itr->second->state = PETSPELL_REMOVED;
1529
1530    RemoveAurasDueToSpell(spell_id);
1531}
1532
1533bool Pet::_removeSpell(uint16 spell_id)
1534{
1535    PetSpellMap::iterator itr = m_spells.find(spell_id);
1536    if (itr != m_spells.end())
1537    {
1538        delete itr->second;
1539        m_spells.erase(itr);
1540        return true;
1541    }
1542    return false;
1543}
1544
1545void Pet::InitPetCreateSpells()
1546{
1547    m_charmInfo->InitPetActionBar();
1548
1549    m_spells.clear();
1550    int32 usedtrainpoints = 0, petspellid;
1551    PetCreateSpellEntry const* CreateSpells = objmgr.GetPetCreateSpellEntry(GetEntry());
1552    if(CreateSpells)
1553    {
1554        for(uint8 i = 0; i < 4; i++)
1555        {
1556            if(!CreateSpells->spellid[i])
1557                break;
1558
1559            SpellEntry const *learn_spellproto = sSpellStore.LookupEntry(CreateSpells->spellid[i]);
1560            if(!learn_spellproto)
1561                continue;
1562
1563            if(learn_spellproto->Effect[0] == SPELL_EFFECT_LEARN_SPELL || learn_spellproto->Effect[0] == SPELL_EFFECT_LEARN_PET_SPELL)
1564            {
1565                petspellid = learn_spellproto->EffectTriggerSpell[0];
1566                Unit* owner = GetOwner();
1567                if(owner->GetTypeId() == TYPEID_PLAYER && !((Player*)owner)->HasSpell(learn_spellproto->Id))
1568                {
1569                    if(IsPassiveSpell(petspellid))          //learn passive skills when tamed, not sure if thats right
1570                        ((Player*)owner)->learnSpell(learn_spellproto->Id);
1571                    else
1572                        AddTeachSpell(learn_spellproto->EffectTriggerSpell[0], learn_spellproto->Id);
1573                }
1574            }
1575            else
1576                petspellid = learn_spellproto->Id;
1577
1578            addSpell(petspellid);
1579
1580            SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(learn_spellproto->EffectTriggerSpell[0]);
1581            SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(learn_spellproto->EffectTriggerSpell[0]);
1582
1583            for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
1584            {
1585                usedtrainpoints += _spell_idx->second->reqtrainpoints;
1586                break;
1587            }
1588        }
1589    }
1590
1591    LearnPetPassives();
1592
1593    CastPetAuras(false);
1594
1595    SetTP(-usedtrainpoints);
1596}
1597
1598void Pet::CheckLearning(uint32 spellid)
1599{
1600                                                            //charmed case -> prevent crash
1601    if(GetTypeId() == TYPEID_PLAYER || getPetType() != HUNTER_PET)
1602        return;
1603
1604    Unit* owner = GetOwner();
1605
1606    if(m_teachspells.empty() || !owner || owner->GetTypeId() != TYPEID_PLAYER)
1607        return;
1608
1609    TeachSpellMap::iterator itr = m_teachspells.find(spellid);
1610    if(itr == m_teachspells.end())
1611        return;
1612
1613    if(urand(0, 100) < 10)
1614    {
1615        ((Player*)owner)->learnSpell(itr->second);
1616        m_teachspells.erase(itr);
1617    }
1618}
1619
1620uint32 Pet::resetTalentsCost() const
1621{
1622    uint32 days = (sWorld.GetGameTime() - m_resetTalentsTime)/DAY;
1623
1624    // The first time reset costs 10 silver; after 1 day cost is reset to 10 silver
1625    if(m_resetTalentsCost < 10*SILVER || days > 0)
1626        return 10*SILVER;
1627    // then 50 silver
1628    else if(m_resetTalentsCost < 50*SILVER)
1629        return 50*SILVER;
1630    // then 1 gold
1631    else if(m_resetTalentsCost < 1*GOLD)
1632        return 1*GOLD;
1633    // then increasing at a rate of 1 gold; cap 10 gold
1634    else
1635        return (m_resetTalentsCost + 1*GOLD > 10*GOLD ? 10*GOLD : m_resetTalentsCost + 1*GOLD);
1636}
1637
1638void Pet::ToggleAutocast(uint32 spellid, bool apply)
1639{
1640    if(IsPassiveSpell(spellid))
1641        return;
1642
1643    PetSpellMap::const_iterator itr = m_spells.find((uint16)spellid);
1644
1645    int i;
1646
1647    if(apply)
1648    {
1649        for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; i++);
1650        if (i == m_autospells.size())
1651        {
1652            m_autospells.push_back(spellid);
1653            itr->second->active = ACT_ENABLED;
1654            itr->second->state = PETSPELL_CHANGED;
1655        }
1656    }
1657    else
1658    {
1659        AutoSpellList::iterator itr2 = m_autospells.begin();
1660        for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; i++, itr2++);
1661        if (i < m_autospells.size())
1662        {
1663            m_autospells.erase(itr2);
1664            itr->second->active = ACT_DISABLED;
1665            itr->second->state = PETSPELL_CHANGED;
1666        }
1667    }
1668}
1669
1670bool Pet::Create(uint32 guidlow, Map *map, uint32 Entry, uint32 pet_number)
1671{
1672    SetMapId(map->GetId());
1673    SetInstanceId(map->GetInstanceId());
1674
1675    Object::_Create(guidlow, pet_number, HIGHGUID_PET);
1676
1677    m_DBTableGuid = guidlow;
1678    m_originalEntry = Entry;
1679
1680    if(!InitEntry(Entry))
1681        return false;
1682
1683    SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE );
1684    SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 );
1685
1686    if(getPetType() == MINI_PET)                            // always non-attackable
1687        SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
1688
1689    return true;
1690}
1691
1692bool Pet::HasSpell(uint32 spell) const
1693{
1694    return (m_spells.find(spell) != m_spells.end());
1695}
1696
1697// Get all passive spells in our skill line
1698void Pet::LearnPetPassives()
1699{
1700    CreatureInfo const* cInfo = GetCreatureInfo();
1701    if(!cInfo)
1702        return;
1703
1704    CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cInfo->family);
1705    if(!cFamily)
1706        return;
1707
1708    PetFamilySpellsStore::const_iterator petStore = sPetFamilySpellsStore.find(cFamily->ID);
1709    if(petStore != sPetFamilySpellsStore.end())
1710    {
1711        for(PetFamilySpellsSet::const_iterator petSet = petStore->second.begin(); petSet != petStore->second.end(); ++petSet)
1712            addSpell(*petSet, ACT_DECIDE, PETSPELL_NEW, 0xffff, PETSPELL_FAMILY);
1713    }
1714}
1715
1716void Pet::CastPetAuras(bool current)
1717{
1718    Unit* owner = GetOwner();
1719    if(!owner)
1720        return;
1721
1722    if(getPetType() != HUNTER_PET && (getPetType() != SUMMON_PET || owner->getClass() != CLASS_WARLOCK))
1723        return;
1724
1725    for(PetAuraSet::iterator itr = owner->m_petAuras.begin(); itr != owner->m_petAuras.end(); )
1726    {
1727        PetAura const* pa = *itr;
1728        ++itr;
1729
1730        if(!current && pa->IsRemovedOnChangePet())
1731            owner->RemovePetAura(pa);
1732        else
1733            CastPetAura(pa);
1734    }
1735}
1736
1737void Pet::CastPetAura(PetAura const* aura)
1738{
1739    uint16 auraId = aura->GetAura(GetEntry());
1740    if(!auraId)
1741        return;
1742
1743    if(auraId == 35696)                                       // Demonic Knowledge
1744    {
1745        int32 basePoints = int32(aura->GetDamage() * (GetStat(STAT_STAMINA) + GetStat(STAT_INTELLECT)) / 100);
1746        CastCustomSpell(this,auraId,&basePoints, NULL, NULL, true );
1747    }
1748    else
1749        CastSpell(this, auraId, true);
1750}
Note: See TracBrowser for help on using the browser.