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

Revision 149, 59.3 kB (checked in by yumileroy, 17 years ago)

[svn] *Implement new player conditions CONDITION_NO_AURA, CONDITION_ACTIVE_EVENT
* Default behaviour of pets for creatures changed to REACT_DEFENSIVE
* Disallowed sending wrapped items as COD
* Prevent loading and saving single target auras for pet in same way as already implemented for player
* Correctly limit use some flask types to zones.
* Fixed extracting common.MPQ under *nix
* Many small xleanups and fixes.
** mangos merge rev.

TEST REV so be careful of creepy crawly bugs!

Original author: KingPin?
Date: 2008-11-02 16:53:46-06:00

Line 
1/*
2 * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
3 *
4 * Copyright (C) 2008 Trinity <http://www.trinitycore.org/>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#include "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                        /// do not load single target auras (unless they were cast by the player)
1370                        if (caster_guid != GetGUID() && IsSingleTargetSpell(spellproto))
1371                                continue;
1372
1373            Aura* aura = CreateAura(spellproto, effindex, NULL, this, NULL);
1374
1375            if(!damage)
1376                damage = aura->GetModifier()->m_amount;
1377            aura->SetLoadedState(caster_guid,damage,maxduration,remaintime,remaincharges);
1378            AddAura(aura);
1379        }
1380        while( result->NextRow() );
1381
1382        delete result;
1383    }
1384}
1385
1386void Pet::_SaveAuras()
1387{
1388    CharacterDatabase.PExecute("DELETE FROM pet_aura WHERE guid = '%u'",m_charmInfo->GetPetNumber());
1389
1390    AuraMap const& auras = GetAuras();
1391    for(AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
1392    {
1393        // skip all auras from spell that apply at cast SPELL_AURA_MOD_SHAPESHIFT or pet area auras.
1394        SpellEntry const *spellInfo = itr->second->GetSpellProto();
1395        uint8 i;
1396        for (i = 0; i < 3; i++)
1397            if (spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_STEALTH ||
1398                spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_OWNER ||
1399                spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_PET )
1400                break;
1401
1402        if (i != 3)
1403                        continue;
1404
1405                if(itr->second->IsPassive())
1406                        continue;
1407
1408                /// do not save single target auras (unless they were cast by the player)
1409                if (itr->second->GetCasterGUID() != GetGUID() && IsSingleTargetSpell(spellInfo))
1410                        continue;
1411
1412                CharacterDatabase.PExecute("INSERT INTO pet_aura (guid,caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges) "
1413                        "VALUES ('%u', '" I64FMTD "', '%u', '%u', '%d', '%d', '%d', '%d')",
1414                        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));
1415    }
1416}
1417
1418bool Pet::addSpell(uint16 spell_id, uint16 active, PetSpellState state, uint16 slot_id, PetSpellType type)
1419{
1420    SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
1421    if (!spellInfo)
1422    {
1423        // do pet spell book cleanup
1424        if(state == PETSPELL_UNCHANGED)                     // spell load case
1425        {
1426            sLog.outError("Pet::addSpell: Non-existed in SpellStore spell #%u request, deleting for all pets in `pet_spell`.",spell_id);
1427            CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE spell = '%u'",spell_id);
1428        }
1429        else
1430            sLog.outError("Pet::addSpell: Non-existed in SpellStore spell #%u request.",spell_id);
1431
1432        return false;
1433    }
1434
1435    PetSpellMap::iterator itr = m_spells.find(spell_id);
1436    if (itr != m_spells.end())
1437    {
1438        if (itr->second->state == PETSPELL_REMOVED)
1439        {
1440            delete itr->second;
1441            m_spells.erase(itr);
1442            state = PETSPELL_CHANGED;
1443        }
1444        else if (state == PETSPELL_UNCHANGED && itr->second->state != PETSPELL_UNCHANGED)
1445        {
1446            // can be in case spell loading but learned at some previous spell loading
1447            itr->second->state = PETSPELL_UNCHANGED;
1448            return false;
1449        }
1450        else
1451            return false;
1452    }
1453
1454    uint32 oldspell_id = 0;
1455
1456    PetSpell *newspell = new PetSpell;
1457    newspell->state = state;
1458    newspell->type = type;
1459
1460    if(active == ACT_DECIDE)                                //active was not used before, so we save it's autocast/passive state here
1461    {
1462        if(IsPassiveSpell(spell_id))
1463            newspell->active = ACT_PASSIVE;
1464        else
1465            newspell->active = ACT_DISABLED;
1466    }
1467    else
1468        newspell->active = active;
1469
1470    uint32 chainstart = spellmgr.GetFirstSpellInChain(spell_id);
1471
1472    for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); itr++)
1473    {
1474        if(itr->second->state == PETSPELL_REMOVED) continue;
1475
1476        if(spellmgr.GetFirstSpellInChain(itr->first) == chainstart)
1477        {
1478            slot_id = itr->second->slotId;
1479            newspell->active = itr->second->active;
1480
1481            if(newspell->active == ACT_ENABLED)
1482                ToggleAutocast(itr->first, false);
1483
1484            oldspell_id = itr->first;
1485            removeSpell(itr->first);
1486        }
1487    }
1488
1489    uint16 tmpslot=slot_id;
1490
1491    if (tmpslot == 0xffff)
1492    {
1493        uint16 maxid = 0;
1494        PetSpellMap::iterator itr;
1495        for (itr = m_spells.begin(); itr != m_spells.end(); ++itr)
1496        {
1497            if(itr->second->state == PETSPELL_REMOVED) continue;
1498            if (itr->second->slotId > maxid) maxid = itr->second->slotId;
1499        }
1500        tmpslot = maxid + 1;
1501    }
1502
1503    newspell->slotId = tmpslot;
1504    m_spells[spell_id] = newspell;
1505
1506    if (IsPassiveSpell(spell_id))
1507        CastSpell(this, spell_id, true);
1508    else if(state == PETSPELL_NEW)
1509        m_charmInfo->AddSpellToAB(oldspell_id, spell_id);
1510
1511    if(newspell->active == ACT_ENABLED)
1512        ToggleAutocast(spell_id, true);
1513
1514    return true;
1515}
1516
1517bool Pet::learnSpell(uint16 spell_id)
1518{
1519    // prevent duplicated entires in spell book
1520    if (!addSpell(spell_id))
1521        return false;
1522
1523    Unit* owner = GetOwner();
1524    if(owner->GetTypeId()==TYPEID_PLAYER)
1525        ((Player*)owner)->PetSpellInitialize();
1526    return true;
1527}
1528
1529void Pet::removeSpell(uint16 spell_id)
1530{
1531    PetSpellMap::iterator itr = m_spells.find(spell_id);
1532    if (itr == m_spells.end())
1533        return;
1534
1535    if(itr->second->state == PETSPELL_REMOVED)
1536        return;
1537
1538    if(itr->second->state == PETSPELL_NEW)
1539    {
1540        delete itr->second;
1541        m_spells.erase(itr);
1542    }
1543    else
1544        itr->second->state = PETSPELL_REMOVED;
1545
1546    RemoveAurasDueToSpell(spell_id);
1547}
1548
1549bool Pet::_removeSpell(uint16 spell_id)
1550{
1551    PetSpellMap::iterator itr = m_spells.find(spell_id);
1552    if (itr != m_spells.end())
1553    {
1554        delete itr->second;
1555        m_spells.erase(itr);
1556        return true;
1557    }
1558    return false;
1559}
1560
1561void Pet::InitPetCreateSpells()
1562{
1563    m_charmInfo->InitPetActionBar();
1564
1565    m_spells.clear();
1566    int32 usedtrainpoints = 0, petspellid;
1567    PetCreateSpellEntry const* CreateSpells = objmgr.GetPetCreateSpellEntry(GetEntry());
1568    if(CreateSpells)
1569    {
1570        for(uint8 i = 0; i < 4; i++)
1571        {
1572            if(!CreateSpells->spellid[i])
1573                break;
1574
1575            SpellEntry const *learn_spellproto = sSpellStore.LookupEntry(CreateSpells->spellid[i]);
1576            if(!learn_spellproto)
1577                continue;
1578
1579            if(learn_spellproto->Effect[0] == SPELL_EFFECT_LEARN_SPELL || learn_spellproto->Effect[0] == SPELL_EFFECT_LEARN_PET_SPELL)
1580            {
1581                petspellid = learn_spellproto->EffectTriggerSpell[0];
1582                Unit* owner = GetOwner();
1583                if(owner->GetTypeId() == TYPEID_PLAYER && !((Player*)owner)->HasSpell(learn_spellproto->Id))
1584                {
1585                    if(IsPassiveSpell(petspellid))          //learn passive skills when tamed, not sure if thats right
1586                        ((Player*)owner)->learnSpell(learn_spellproto->Id);
1587                    else
1588                        AddTeachSpell(learn_spellproto->EffectTriggerSpell[0], learn_spellproto->Id);
1589                }
1590            }
1591            else
1592                petspellid = learn_spellproto->Id;
1593
1594            addSpell(petspellid);
1595
1596            SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(learn_spellproto->EffectTriggerSpell[0]);
1597            SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(learn_spellproto->EffectTriggerSpell[0]);
1598
1599            for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
1600            {
1601                usedtrainpoints += _spell_idx->second->reqtrainpoints;
1602                break;
1603            }
1604        }
1605    }
1606
1607    LearnPetPassives();
1608
1609    CastPetAuras(false);
1610
1611    SetTP(-usedtrainpoints);
1612}
1613
1614void Pet::CheckLearning(uint32 spellid)
1615{
1616                                                            //charmed case -> prevent crash
1617    if(GetTypeId() == TYPEID_PLAYER || getPetType() != HUNTER_PET)
1618        return;
1619
1620    Unit* owner = GetOwner();
1621
1622    if(m_teachspells.empty() || !owner || owner->GetTypeId() != TYPEID_PLAYER)
1623        return;
1624
1625    TeachSpellMap::iterator itr = m_teachspells.find(spellid);
1626    if(itr == m_teachspells.end())
1627        return;
1628
1629    if(urand(0, 100) < 10)
1630    {
1631        ((Player*)owner)->learnSpell(itr->second);
1632        m_teachspells.erase(itr);
1633    }
1634}
1635
1636uint32 Pet::resetTalentsCost() const
1637{
1638    uint32 days = (sWorld.GetGameTime() - m_resetTalentsTime)/DAY;
1639
1640    // The first time reset costs 10 silver; after 1 day cost is reset to 10 silver
1641    if(m_resetTalentsCost < 10*SILVER || days > 0)
1642        return 10*SILVER;
1643    // then 50 silver
1644    else if(m_resetTalentsCost < 50*SILVER)
1645        return 50*SILVER;
1646    // then 1 gold
1647    else if(m_resetTalentsCost < 1*GOLD)
1648        return 1*GOLD;
1649    // then increasing at a rate of 1 gold; cap 10 gold
1650    else
1651        return (m_resetTalentsCost + 1*GOLD > 10*GOLD ? 10*GOLD : m_resetTalentsCost + 1*GOLD);
1652}
1653
1654void Pet::ToggleAutocast(uint32 spellid, bool apply)
1655{
1656    if(IsPassiveSpell(spellid))
1657        return;
1658
1659    PetSpellMap::const_iterator itr = m_spells.find((uint16)spellid);
1660
1661    int i;
1662
1663    if(apply)
1664    {
1665        for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; i++);
1666        if (i == m_autospells.size())
1667        {
1668            m_autospells.push_back(spellid);
1669            itr->second->active = ACT_ENABLED;
1670            itr->second->state = PETSPELL_CHANGED;
1671        }
1672    }
1673    else
1674    {
1675        AutoSpellList::iterator itr2 = m_autospells.begin();
1676        for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; i++, itr2++);
1677        if (i < m_autospells.size())
1678        {
1679            m_autospells.erase(itr2);
1680            itr->second->active = ACT_DISABLED;
1681            itr->second->state = PETSPELL_CHANGED;
1682        }
1683    }
1684}
1685
1686bool Pet::Create(uint32 guidlow, Map *map, uint32 Entry, uint32 pet_number)
1687{
1688    SetMapId(map->GetId());
1689    SetInstanceId(map->GetInstanceId());
1690
1691    Object::_Create(guidlow, pet_number, HIGHGUID_PET);
1692
1693    m_DBTableGuid = guidlow;
1694    m_originalEntry = Entry;
1695
1696    if(!InitEntry(Entry))
1697        return false;
1698
1699    SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE );
1700    SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 );
1701
1702    if(getPetType() == MINI_PET)                            // always non-attackable
1703        SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
1704
1705    return true;
1706}
1707
1708bool Pet::HasSpell(uint32 spell) const
1709{
1710    return (m_spells.find(spell) != m_spells.end());
1711}
1712
1713// Get all passive spells in our skill line
1714void Pet::LearnPetPassives()
1715{
1716    CreatureInfo const* cInfo = GetCreatureInfo();
1717    if(!cInfo)
1718        return;
1719
1720    CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cInfo->family);
1721    if(!cFamily)
1722        return;
1723
1724    PetFamilySpellsStore::const_iterator petStore = sPetFamilySpellsStore.find(cFamily->ID);
1725    if(petStore != sPetFamilySpellsStore.end())
1726    {
1727        for(PetFamilySpellsSet::const_iterator petSet = petStore->second.begin(); petSet != petStore->second.end(); ++petSet)
1728            addSpell(*petSet, ACT_DECIDE, PETSPELL_NEW, 0xffff, PETSPELL_FAMILY);
1729    }
1730}
1731
1732void Pet::CastPetAuras(bool current)
1733{
1734    Unit* owner = GetOwner();
1735    if(!owner)
1736        return;
1737
1738    if(getPetType() != HUNTER_PET && (getPetType() != SUMMON_PET || owner->getClass() != CLASS_WARLOCK))
1739        return;
1740
1741    for(PetAuraSet::iterator itr = owner->m_petAuras.begin(); itr != owner->m_petAuras.end(); )
1742    {
1743        PetAura const* pa = *itr;
1744        ++itr;
1745
1746        if(!current && pa->IsRemovedOnChangePet())
1747            owner->RemovePetAura(pa);
1748        else
1749            CastPetAura(pa);
1750    }
1751}
1752
1753void Pet::CastPetAura(PetAura const* aura)
1754{
1755    uint16 auraId = aura->GetAura(GetEntry());
1756    if(!auraId)
1757        return;
1758
1759    if(auraId == 35696)                                       // Demonic Knowledge
1760    {
1761        int32 basePoints = int32(aura->GetDamage() * (GetStat(STAT_STAMINA) + GetStat(STAT_INTELLECT)) / 100);
1762        CastCustomSpell(this,auraId,&basePoints, NULL, NULL, true );
1763    }
1764    else
1765        CastSpell(this, auraId, true);
1766}
Note: See TracBrowser for help on using the browser.