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

Revision 120, 59.0 kB (checked in by yumileroy, 17 years ago)

[svn] * Allow WorldObjects? to keep the grid active, and prevent it from being unloaded. This can be done through calling WorldObject::setActive(bool) from the scripting library. Note that entire instances are still unloaded if no player is present on that map to save resources. This behavior can be changed if the need arises.

Original author: w12x
Date: 2008-10-27 08:41:55-05:00

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