2 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 #include "Database/DatabaseEnv.h"
22 #include "WorldSession.h"
23 #include "WorldPacket.h"
24 #include "ObjectMgr.h"
27 #include "MapManager.h"
29 #include "SpellAuras.h"
30 #include "CreatureAI.h"
34 char const* petTypeSuffix
[MAX_PET_TYPE
] =
36 "'s Minion", // SUMMON_PET
37 "'s Pet", // HUNTER_PET
38 "'s Guardian", // GUARDIAN_PET
39 "'s Companion" // MINI_PET
42 Pet::Pet(PetType type
) :
43 Creature(), m_petType(type
), m_removed(false), m_happinessTimer(7500), m_duration(0), m_bonusdamage(0),
44 m_resetTalentsCost(0), m_resetTalentsTime(0), m_usedTalentCount(0), m_auraUpdateMask(0), m_loading(false),
51 // pets always have a charminfo, even if they are not actually charmed
52 CharmInfo
* charmInfo
= InitCharmInfo(this);
54 if(type
== MINI_PET
) // always passive
55 charmInfo
->SetReactState(REACT_PASSIVE
);
56 else if(type
== GUARDIAN_PET
) // always aggressive
57 charmInfo
->SetReactState(REACT_AGGRESSIVE
);
62 if(m_uint32Values
) // only for fully created Object
64 for (PetSpellMap::iterator i
= m_spells
.begin(); i
!= m_spells
.end(); ++i
)
66 ObjectAccessor::Instance().RemoveObject(this);
69 delete m_declinedname
;
72 void Pet::AddToWorld()
74 ///- Register the pet for guid lookup
75 if(!IsInWorld()) ObjectAccessor::Instance().AddObject(this);
79 void Pet::RemoveFromWorld()
81 ///- Remove the pet from the accessor
82 if(IsInWorld()) ObjectAccessor::Instance().RemoveObject(this);
83 ///- Don't call the function for Creature, normal mobs + totems go in a different storage
84 Unit::RemoveFromWorld();
87 bool Pet::LoadPetFromDB( Player
* owner
, uint32 petentry
, uint32 petnumber
, bool current
)
91 uint32 ownerid
= owner
->GetGUIDLow();
96 // known petnumber entry 0 1 2(?) 3 4 5 6 7 8(?) 9 10 11 12 13 14 15 16 17 18 19 20
97 result
= CharacterDatabase
.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, talentpoints, 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
);
99 // 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
100 result
= CharacterDatabase
.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, talentpoints, 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
);
102 // known petentry entry (unique for summoned pet, but non unique for hunter pet (only from current or not stabled pets)
103 // 0 1 2(?) 3 4 5 6 7 8(?) 9 10 11 12 13 14 15 16 17 18 19 20
104 result
= CharacterDatabase
.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, talentpoints, 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
);
106 // any current or other non-stabled pet (for hunter "call pet")
107 // 0 1 2(?) 3 4 5 6 7 8(?) 9 10 11 12 13 14 15 16 17 18 19 20
108 result
= CharacterDatabase
.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, talentpoints, 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
);
113 Field
*fields
= result
->Fetch();
115 // update for case of current pet "slot = 0"
116 petentry
= fields
[1].GetUInt32();
123 uint32 summon_spell_id
= fields
[19].GetUInt32();
124 SpellEntry
const* spellInfo
= sSpellStore
.LookupEntry(summon_spell_id
);
126 bool is_temporary_summoned
= spellInfo
&& GetSpellDuration(spellInfo
) > 0;
128 // check temporary summoned pets like mage water elemental
129 if(current
&& is_temporary_summoned
)
135 Map
*map
= owner
->GetMap();
136 uint32 guid
= objmgr
.GenerateLowGuid(HIGHGUID_PET
);
137 uint32 pet_number
= fields
[0].GetUInt32();
138 if(!Create(guid
, map
, owner
->GetPhaseMask(), petentry
, pet_number
))
145 owner
->GetClosePoint(px
, py
, pz
, GetObjectSize(), PET_FOLLOW_DIST
, PET_FOLLOW_ANGLE
);
147 Relocate(px
, py
, pz
, owner
->GetOrientation());
149 if(!IsPositionValid())
151 sLog
.outError("ERROR: Pet (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)",
152 GetGUIDLow(), GetEntry(), GetPositionX(), GetPositionY());
157 setPetType(PetType(fields
[20].GetUInt8()));
158 SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE
, owner
->getFaction());
159 SetUInt32Value(UNIT_CREATED_BY_SPELL
, summon_spell_id
);
161 CreatureInfo
const *cinfo
= GetCreatureInfo();
162 if(cinfo
->type
== CREATURE_TYPE_CRITTER
)
165 map
->Add((Creature
*)this);
170 if(getPetType() == HUNTER_PET
|| (getPetType() == SUMMON_PET
&& cinfo
->type
== CREATURE_TYPE_DEMON
&& owner
->getClass() == CLASS_WARLOCK
))
171 m_charmInfo
->SetPetNumber(pet_number
, true);
173 m_charmInfo
->SetPetNumber(pet_number
, false);
175 SetOwnerGUID(owner
->GetGUID());
176 SetDisplayId(fields
[3].GetUInt32());
177 SetNativeDisplayId(fields
[3].GetUInt32());
178 uint32 petlevel
= fields
[4].GetUInt32();
179 SetUInt32Value(UNIT_NPC_FLAGS
, 0);
180 SetName(fields
[9].GetString());
185 petlevel
=owner
->getLevel();
187 SetUInt32Value(UNIT_FIELD_BYTES_0
, 2048);
188 SetUInt32Value(UNIT_FIELD_FLAGS
, UNIT_FLAG_PVP_ATTACKABLE
);
189 // this enables popup window (pet dismiss, cancel)
192 SetUInt32Value(UNIT_FIELD_BYTES_0
, 0x02020100);
193 SetByteValue(UNIT_FIELD_BYTES_1
, 1, fields
[7].GetUInt32());
194 SetByteValue(UNIT_FIELD_BYTES_2
, 0, SHEATH_STATE_MELEE
);
195 SetByteValue(UNIT_FIELD_BYTES_2
, 2, fields
[10].GetBool() ? UNIT_RENAME_NOT_ALLOWED
: UNIT_RENAME_ALLOWED
);
197 SetUInt32Value(UNIT_FIELD_FLAGS
, UNIT_FLAG_PVP_ATTACKABLE
);
198 // this enables popup window (pet abandon, cancel)
199 SetMaxPower(POWER_HAPPINESS
, GetCreatePowers(POWER_HAPPINESS
));
200 SetPower(POWER_HAPPINESS
, fields
[13].GetUInt32());
201 setPowerType(POWER_FOCUS
);
204 sLog
.outError("Pet have incorrect type (%u) for pet loading.", getPetType());
207 InitStatsForLevel(petlevel
);
208 SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP
, time(NULL
));
209 SetUInt32Value(UNIT_FIELD_PETEXPERIENCE
, fields
[5].GetUInt32());
210 SetCreatorGUID(owner
->GetGUID());
212 m_charmInfo
->SetReactState(ReactStates(fields
[6].GetUInt8()));
214 uint32 savedhealth
= fields
[11].GetUInt32();
215 uint32 savedmana
= fields
[12].GetUInt32();
217 // set current pet as current
218 if(fields
[8].GetUInt32() != 0)
220 CharacterDatabase
.BeginTransaction();
221 CharacterDatabase
.PExecute("UPDATE character_pet SET slot = '3' WHERE owner = '%u' AND slot = '0' AND id <> '%u'", ownerid
, m_charmInfo
->GetPetNumber());
222 CharacterDatabase
.PExecute("UPDATE character_pet SET slot = '0' WHERE owner = '%u' AND id = '%u'", ownerid
, m_charmInfo
->GetPetNumber());
223 CharacterDatabase
.CommitTransaction();
226 if(!is_temporary_summoned
)
228 // permanent controlled pets store state in DB
229 Tokens tokens
= StrSplit(fields
[14].GetString(), " ");
231 if(tokens
.size() != 20)
238 Tokens::iterator iter
;
239 for(iter
= tokens
.begin(), index
= 0; index
< 10; ++iter
, ++index
)
241 m_charmInfo
->GetActionBarEntry(index
)->Type
= atol((*iter
).c_str());
243 m_charmInfo
->GetActionBarEntry(index
)->SpellOrAction
= atol((*iter
).c_str());
247 tokens
= StrSplit(fields
[15].GetString(), " ");
248 for (iter
= tokens
.begin(), index
= 0; index
< 4; ++iter
, ++index
)
250 uint32 tmp
= atol((*iter
).c_str());
255 AddTeachSpell(tmp
, atol((*iter
).c_str()));
261 // since last save (in seconds)
262 uint32 timediff
= (time(NULL
) - fields
[16].GetUInt32());
264 m_resetTalentsCost
= fields
[17].GetUInt32();
265 m_resetTalentsTime
= fields
[18].GetUInt64();
269 //load spells/cooldowns/auras
270 SetCanModifyStats(true);
271 _LoadAuras(timediff
);
274 if(is_temporary_summoned
)
276 // Temporary summoned pets always have initial spell list at load
277 InitPetCreateSpells();
282 CastPetAuras(current
);
285 if(getPetType() == SUMMON_PET
&& !current
) //all (?) summon pets come with full health when called, but not when they are current
287 SetHealth(GetMaxHealth());
288 SetPower(POWER_MANA
, GetMaxPower(POWER_MANA
));
292 SetHealth(savedhealth
> GetMaxHealth() ? GetMaxHealth() : savedhealth
);
293 SetPower(POWER_MANA
, savedmana
> GetMaxPower(POWER_MANA
) ? GetMaxPower(POWER_MANA
) : savedmana
);
297 map
->Add((Creature
*)this);
299 // Spells should be loaded after pet is added to map, because in CanCast is check on it
301 _LoadSpellCooldowns();
303 owner
->SetPet(this); // in DB stored only full controlled creature
304 sLog
.outDebug("New Pet has guid %u", GetGUIDLow());
306 if(owner
->GetTypeId() == TYPEID_PLAYER
)
308 ((Player
*)owner
)->PetSpellInitialize();
309 if(((Player
*)owner
)->GetGroup())
310 ((Player
*)owner
)->SetGroupUpdateFlag(GROUP_UPDATE_PET
);
313 if(owner
->GetTypeId() == TYPEID_PLAYER
&& getPetType() == HUNTER_PET
)
315 result
= CharacterDatabase
.PQuery("SELECT genitive, dative, accusative, instrumental, prepositional FROM character_pet_declinedname WHERE owner = '%u' AND id = '%u'", owner
->GetGUIDLow(), GetCharmInfo()->GetPetNumber());
320 delete m_declinedname
;
322 m_declinedname
= new DeclinedName
;
323 Field
*fields
= result
->Fetch();
324 for(int i
= 0; i
< MAX_DECLINED_NAME_CASES
; ++i
)
326 m_declinedname
->name
[i
] = fields
[i
].GetCppString();
335 void Pet::SavePetToDB(PetSaveMode mode
)
340 // save only fully controlled creature
344 uint32 curhealth
= GetHealth();
345 uint32 curmana
= GetPower(POWER_MANA
);
349 case PET_SAVE_IN_STABLE_SLOT_1
:
350 case PET_SAVE_IN_STABLE_SLOT_2
:
351 case PET_SAVE_NOT_IN_SLOT
:
355 //only alive hunter pets get auras saved, the others don't
356 if(!(getPetType() == HUNTER_PET
&& isAlive()))
364 _SaveSpellCooldowns();
369 case PET_SAVE_AS_CURRENT
:
370 case PET_SAVE_IN_STABLE_SLOT_1
:
371 case PET_SAVE_IN_STABLE_SLOT_2
:
372 case PET_SAVE_NOT_IN_SLOT
:
374 uint32 owner
= GUID_LOPART(GetOwnerGUID());
375 std::string name
= m_name
;
376 CharacterDatabase
.escape_string(name
);
377 CharacterDatabase
.BeginTransaction();
378 // remove current data
379 CharacterDatabase
.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND id = '%u'", owner
,m_charmInfo
->GetPetNumber() );
381 // prevent duplicate using slot (except PET_SAVE_NOT_IN_SLOT)
382 if(mode
!=PET_SAVE_NOT_IN_SLOT
)
383 CharacterDatabase
.PExecute("UPDATE character_pet SET slot = 3 WHERE owner = '%u' AND slot = '%u'", owner
, uint32(mode
) );
385 // prevent existence another hunter pet in PET_SAVE_AS_CURRENT and PET_SAVE_NOT_IN_SLOT
386 if(getPetType()==HUNTER_PET
&& (mode
==PET_SAVE_AS_CURRENT
||mode
==PET_SAVE_NOT_IN_SLOT
))
387 CharacterDatabase
.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND (slot = '0' OR slot = '3')", owner
);
389 std::ostringstream ss
;
390 ss
<< "INSERT INTO character_pet ( id, entry, owner, modelid, level, exp, Reactstate, talentpoints, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType) "
392 << m_charmInfo
->GetPetNumber() << ", "
393 << GetEntry() << ", "
395 << GetNativeDisplayId() << ", "
396 << getLevel() << ", "
397 << GetUInt32Value(UNIT_FIELD_PETEXPERIENCE
) << ", "
398 << uint32(m_charmInfo
->GetReactState()) << ", "
399 << uint32(GetFreeTalentPoints()) << ", "
400 << uint32(mode
) << ", '"
401 << name
.c_str() << "', "
402 << uint32((GetByteValue(UNIT_FIELD_BYTES_2
, 2) == UNIT_RENAME_ALLOWED
)?0:1) << ", "
403 << (curhealth
<1?1:curhealth
) << ", "
405 << GetPower(POWER_HAPPINESS
) << ", '";
407 for(uint32 i
= 0; i
< 10; i
++)
408 ss
<< uint32(m_charmInfo
->GetActionBarEntry(i
)->Type
) << " " << uint32(m_charmInfo
->GetActionBarEntry(i
)->SpellOrAction
) << " ";
411 //save spells the pet can teach to it's Master
414 for(TeachSpellMap::iterator itr
= m_teachspells
.begin(); i
< 4 && itr
!= m_teachspells
.end(); ++i
, ++itr
)
415 ss
<< itr
->first
<< " " << itr
->second
<< " ";
417 ss
<< uint32(0) << " " << uint32(0) << " ";
421 << time(NULL
) << ", "
422 << uint32(m_resetTalentsCost
) << ", "
423 << uint64(m_resetTalentsTime
) << ", "
424 << GetUInt32Value(UNIT_CREATED_BY_SPELL
) << ", "
425 << uint32(getPetType()) << ")";
427 CharacterDatabase
.Execute( ss
.str().c_str() );
429 CharacterDatabase
.CommitTransaction();
432 case PET_SAVE_AS_DELETED
:
435 DeleteFromDB(m_charmInfo
->GetPetNumber());
439 sLog
.outError("Unknown pet save/remove mode: %d",mode
);
443 void Pet::DeleteFromDB(uint32 guidlow
)
445 CharacterDatabase
.PExecute("DELETE FROM character_pet WHERE id = '%u'", guidlow
);
446 CharacterDatabase
.PExecute("DELETE FROM character_pet_declinedname WHERE id = '%u'", guidlow
);
447 CharacterDatabase
.PExecute("DELETE FROM pet_aura WHERE guid = '%u'", guidlow
);
448 CharacterDatabase
.PExecute("DELETE FROM pet_spell WHERE guid = '%u'", guidlow
);
449 CharacterDatabase
.PExecute("DELETE FROM pet_spell_cooldown WHERE guid = '%u'", guidlow
);
452 void Pet::setDeathState(DeathState s
) // overwrite virtual Creature::setDeathState and Unit::setDeathState
454 Creature::setDeathState(s
);
455 if(getDeathState()==CORPSE
)
457 //remove summoned pet (no corpse)
458 if(getPetType()==SUMMON_PET
)
459 Remove(PET_SAVE_NOT_IN_SLOT
);
460 // other will despawn at corpse desppawning (Pet::Update code)
463 // pet corpse non lootable and non skinnable
464 SetUInt32Value( UNIT_DYNAMIC_FLAGS
, 0x00 );
465 RemoveFlag (UNIT_FIELD_FLAGS
, UNIT_FLAG_SKINNABLE
);
467 //lose happiness when died and not in BG/Arena
468 MapEntry
const* mapEntry
= sMapStore
.LookupEntry(GetMapId());
469 if(!mapEntry
|| (mapEntry
->map_type
!= MAP_ARENA
&& mapEntry
->map_type
!= MAP_BATTLEGROUND
))
470 ModifyPower(POWER_HAPPINESS
, -HAPPINESS_LEVEL_SIZE
);
472 SetFlag(UNIT_FIELD_FLAGS
, UNIT_FLAG_STUNNED
);
475 else if(getDeathState()==ALIVE
)
477 RemoveFlag(UNIT_FIELD_FLAGS
, UNIT_FLAG_STUNNED
);
482 void Pet::Update(uint32 diff
)
484 if(m_removed
) // pet already removed, just wait in remove queue, no updates
487 switch( m_deathState
)
491 if( m_deathTimer
<= diff
)
493 assert(getPetType()!=SUMMON_PET
&& "Must be already removed.");
494 Remove(PET_SAVE_NOT_IN_SLOT
); //hunters' pets never get removed because of death, NEVER!
501 // unsummon pet that lost owner
502 Unit
* owner
= GetOwner();
503 if(!owner
|| (!IsWithinDistInMap(owner
, OWNER_MAX_DISTANCE
) && (owner
->GetCharmGUID() && (owner
->GetCharmGUID() != GetGUID()))) || (isControlled() && !owner
->GetPetGUID()))
505 Remove(PET_SAVE_NOT_IN_SLOT
, true);
511 if( owner
->GetPetGUID() != GetGUID() )
513 Remove(getPetType()==HUNTER_PET
?PET_SAVE_AS_DELETED
:PET_SAVE_NOT_IN_SLOT
);
520 if(m_duration
> diff
)
524 Remove(getPetType() != SUMMON_PET
? PET_SAVE_AS_DELETED
:PET_SAVE_NOT_IN_SLOT
);
529 if(getPetType() != HUNTER_PET
)
533 if(m_regenTimer
<= diff
)
539 m_regenTimer
-= diff
;
541 if(m_happinessTimer
<= diff
)
544 m_happinessTimer
= 7500;
547 m_happinessTimer
-= diff
;
554 Creature::Update(diff
);
557 void Pet::RegenerateFocus()
559 uint32 curValue
= GetPower(POWER_FOCUS
);
560 uint32 maxValue
= GetMaxPower(POWER_FOCUS
);
562 if (curValue
>= maxValue
)
565 float addvalue
= 24 * sWorld
.getRate(RATE_POWER_FOCUS
);
567 AuraList
const& ModPowerRegenPCTAuras
= GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT
);
568 for(AuraList::const_iterator i
= ModPowerRegenPCTAuras
.begin(); i
!= ModPowerRegenPCTAuras
.end(); ++i
)
569 if ((*i
)->GetModifier()->m_miscvalue
== POWER_FOCUS
)
570 addvalue
*= ((*i
)->GetModifier()->m_amount
+ 100) / 100.0f
;
572 ModifyPower(POWER_FOCUS
, (int32
)addvalue
);
575 void Pet::LooseHappiness()
577 uint32 curValue
= GetPower(POWER_HAPPINESS
);
580 int32 addvalue
= 670; //value is 70/35/17/8/4 (per min) * 1000 / 8 (timer 7.5 secs)
581 if(isInCombat()) //we know in combat happiness fades faster, multiplier guess
582 addvalue
= int32(addvalue
* 1.5);
583 ModifyPower(POWER_HAPPINESS
, -addvalue
);
586 HappinessState
Pet::GetHappinessState()
588 if(GetPower(POWER_HAPPINESS
) < HAPPINESS_LEVEL_SIZE
)
590 else if(GetPower(POWER_HAPPINESS
) >= HAPPINESS_LEVEL_SIZE
* 2)
596 bool Pet::CanTakeMoreActiveSpells(uint32 spellid
)
598 uint8 activecount
= 1;
599 uint32 chainstartstore
[ACTIVE_SPELLS_MAX
];
601 if(IsPassiveSpell(spellid
))
604 chainstartstore
[0] = spellmgr
.GetFirstSpellInChain(spellid
);
606 for (PetSpellMap::iterator itr
= m_spells
.begin(); itr
!= m_spells
.end(); ++itr
)
608 if(IsPassiveSpell(itr
->first
))
611 uint32 chainstart
= spellmgr
.GetFirstSpellInChain(itr
->first
);
615 for(x
= 0; x
< activecount
; x
++)
617 if(chainstart
== chainstartstore
[x
])
621 if(x
== activecount
) //spellchain not yet saved -> add active count
624 if(activecount
> ACTIVE_SPELLS_MAX
)
626 chainstartstore
[x
] = chainstart
;
632 void Pet::Remove(PetSaveMode mode
, bool returnreagent
)
634 Unit
* owner
= GetOwner();
638 if(owner
->GetTypeId()==TYPEID_PLAYER
)
640 ((Player
*)owner
)->RemovePet(this,mode
,returnreagent
);
644 // only if current pet in slot
645 if(owner
->GetPetGUID()==GetGUID())
649 CleanupsBeforeDelete();
650 AddObjectToRemoveList();
654 void Pet::GivePetXP(uint32 xp
)
656 if(getPetType() != HUNTER_PET
)
665 uint32 level
= getLevel();
667 // XP to money conversion processed in Player::RewardQuest
668 if(level
>= sWorld
.getConfig(CONFIG_MAX_PLAYER_LEVEL
))
671 uint32 curXP
= GetUInt32Value(UNIT_FIELD_PETEXPERIENCE
);
672 uint32 nextLvlXP
= GetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP
);
673 uint32 newXP
= curXP
+ xp
;
675 if(newXP
>= nextLvlXP
&& level
+1 > GetOwner()->getLevel())
677 SetUInt32Value(UNIT_FIELD_PETEXPERIENCE
, nextLvlXP
-1);
681 while( newXP
>= nextLvlXP
&& level
< sWorld
.getConfig(CONFIG_MAX_PLAYER_LEVEL
) )
685 SetLevel( level
+ 1 );
686 SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP
, objmgr
.GetXPForLevel(level
+1)/4);
689 nextLvlXP
= GetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP
);
693 SetUInt32Value(UNIT_FIELD_PETEXPERIENCE
, newXP
);
696 void Pet::GivePetLevel(uint32 level
)
701 InitStatsForLevel(level
);
702 InitTalentForLevel();
705 bool Pet::CreateBaseAtCreature(Creature
* creature
)
709 sLog
.outError("CRITICAL ERROR: NULL pointer parsed into CreateBaseAtCreature()");
712 uint32 guid
=objmgr
.GenerateLowGuid(HIGHGUID_PET
);
714 sLog
.outBasic("SetInstanceID()");
715 SetInstanceId(creature
->GetInstanceId());
717 sLog
.outBasic("Create pet");
718 uint32 pet_number
= objmgr
.GeneratePetNumber();
719 if(!Create(guid
, creature
->GetMap(), creature
->GetPhaseMask(), creature
->GetEntry(), pet_number
))
722 Relocate(creature
->GetPositionX(), creature
->GetPositionY(), creature
->GetPositionZ(), creature
->GetOrientation());
724 if(!IsPositionValid())
726 sLog
.outError("ERROR: Pet (guidlow %d, entry %d) not created base at creature. Suggested coordinates isn't valid (X: %f Y: %f)",
727 GetGUIDLow(), GetEntry(), GetPositionX(), GetPositionY());
731 CreatureInfo
const *cinfo
= GetCreatureInfo();
734 sLog
.outError("ERROR: CreateBaseAtCreature() failed, creatureInfo is missing!");
738 if(cinfo
->type
== CREATURE_TYPE_CRITTER
)
740 setPetType(MINI_PET
);
743 SetDisplayId(creature
->GetDisplayId());
744 SetNativeDisplayId(creature
->GetNativeDisplayId());
745 SetMaxPower(POWER_HAPPINESS
, GetCreatePowers(POWER_HAPPINESS
));
746 SetPower(POWER_HAPPINESS
, 166500);
747 setPowerType(POWER_FOCUS
);
748 SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP
, 0);
749 SetUInt32Value(UNIT_FIELD_PETEXPERIENCE
, 0);
750 SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP
, objmgr
.GetXPForLevel(creature
->getLevel())/4);
751 SetUInt32Value(UNIT_NPC_FLAGS
, 0);
753 if(CreatureFamilyEntry
const* cFamily
= sCreatureFamilyStore
.LookupEntry(cinfo
->family
))
754 SetName(cFamily
->Name
[sWorld
.GetDefaultDbcLocale()]);
756 SetName(creature
->GetNameForLocaleIdx(objmgr
.GetDBCLocaleIndex()));
758 if(cinfo
->type
== CREATURE_TYPE_BEAST
)
760 SetUInt32Value(UNIT_FIELD_BYTES_0
, 0x02020100);
761 SetByteValue(UNIT_FIELD_BYTES_2
, 0, SHEATH_STATE_MELEE
);
762 SetByteValue(UNIT_FIELD_BYTES_2
, 2, UNIT_RENAME_ALLOWED
);
763 SetUInt32Value(UNIT_MOD_CAST_SPEED
, creature
->GetUInt32Value(UNIT_MOD_CAST_SPEED
));
768 bool Pet::InitStatsForLevel(uint32 petlevel
)
770 CreatureInfo
const *cinfo
= GetCreatureInfo();
773 Unit
* owner
= GetOwner();
776 sLog
.outError("ERROR: attempt to summon pet (Entry %u) without owner! Attempt terminated.", cinfo
->Entry
);
780 uint32 creature_ID
= (getPetType() == HUNTER_PET
) ? 1 : cinfo
->Entry
;
784 SetMeleeDamageSchool(SpellSchools(cinfo
->dmgschool
));
786 SetModifierValue(UNIT_MOD_ARMOR
, BASE_VALUE
, float(petlevel
*50));
788 SetAttackTime(BASE_ATTACK
, BASE_ATTACK_TIME
);
789 SetAttackTime(OFF_ATTACK
, BASE_ATTACK_TIME
);
790 SetAttackTime(RANGED_ATTACK
, BASE_ATTACK_TIME
);
792 SetFloatValue(UNIT_MOD_CAST_SPEED
, 1.0);
794 CreatureFamilyEntry
const* cFamily
= sCreatureFamilyStore
.LookupEntry(cinfo
->family
);
795 if(cFamily
&& cFamily
->minScale
> 0.0f
&& getPetType()==HUNTER_PET
)
798 if (getLevel() >= cFamily
->maxScaleLevel
)
799 scale
= cFamily
->maxScale
;
800 else if (getLevel() <= cFamily
->minScaleLevel
)
801 scale
= cFamily
->minScale
;
803 scale
= cFamily
->minScale
+ (getLevel() - cFamily
->minScaleLevel
) / cFamily
->maxScaleLevel
* (cFamily
->maxScale
- cFamily
->minScale
);
805 SetFloatValue(OBJECT_FIELD_SCALE_X
, scale
);
809 int32 createResistance
[MAX_SPELL_SCHOOL
] = {0,0,0,0,0,0,0};
811 if(cinfo
&& getPetType() != HUNTER_PET
)
813 createResistance
[SPELL_SCHOOL_HOLY
] = cinfo
->resistance1
;
814 createResistance
[SPELL_SCHOOL_FIRE
] = cinfo
->resistance2
;
815 createResistance
[SPELL_SCHOOL_NATURE
] = cinfo
->resistance3
;
816 createResistance
[SPELL_SCHOOL_FROST
] = cinfo
->resistance4
;
817 createResistance
[SPELL_SCHOOL_SHADOW
] = cinfo
->resistance5
;
818 createResistance
[SPELL_SCHOOL_ARCANE
] = cinfo
->resistance6
;
825 if(owner
->GetTypeId() == TYPEID_PLAYER
)
827 switch(owner
->getClass())
832 //the damage bonus used for pets is either fire or shadow damage, whatever is higher
833 uint32 fire
= owner
->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS
+ SPELL_SCHOOL_FIRE
);
834 uint32 shadow
= owner
->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS
+ SPELL_SCHOOL_SHADOW
);
835 uint32 val
= (fire
> shadow
) ? fire
: shadow
;
837 SetBonusDamage(int32 (val
* 0.15f
));
838 //bonusAP += val * 0.57;
843 //40% damage bonus of mage's frost damage
844 float val
= owner
->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS
+ SPELL_SCHOOL_FROST
) * 0.4;
847 SetBonusDamage( int32(val
));
855 SetBaseWeaponDamage(BASE_ATTACK
, MINDAMAGE
, float(petlevel
- (petlevel
/ 4)) );
856 SetBaseWeaponDamage(BASE_ATTACK
, MAXDAMAGE
, float(petlevel
+ (petlevel
/ 4)) );
858 //SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, float(cinfo->attackpower));
860 PetLevelInfo
const* pInfo
= objmgr
.GetPetLevelInfo(creature_ID
, petlevel
);
861 if(pInfo
) // exist in DB
863 SetCreateHealth(pInfo
->health
);
864 SetCreateMana(pInfo
->mana
);
867 SetModifierValue(UNIT_MOD_ARMOR
, BASE_VALUE
, float(pInfo
->armor
));
869 for(int stat
= 0; stat
< MAX_STATS
; ++stat
)
871 SetCreateStat(Stats(stat
), float(pInfo
->stats
[stat
]));
874 else // not exist in DB, use some default fake data
876 sLog
.outErrorDb("Summoned pet (Entry: %u) not have pet stats data in DB",cinfo
->Entry
);
878 // remove elite bonuses included in DB values
879 SetCreateHealth(uint32(((float(cinfo
->maxhealth
) / cinfo
->maxlevel
) / (1 + 2 * cinfo
->rank
)) * petlevel
) );
880 SetCreateMana( uint32(((float(cinfo
->maxmana
) / cinfo
->maxlevel
) / (1 + 2 * cinfo
->rank
)) * petlevel
) );
882 SetCreateStat(STAT_STRENGTH
, 22);
883 SetCreateStat(STAT_AGILITY
, 22);
884 SetCreateStat(STAT_STAMINA
, 25);
885 SetCreateStat(STAT_INTELLECT
, 28);
886 SetCreateStat(STAT_SPIRIT
, 27);
892 SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP
, objmgr
.GetXPForLevel(petlevel
)/4);
893 learnLevelupSpells();
894 //these formula may not be correct; however, it is designed to be close to what it should be
895 //this makes dps 0.5 of pets level
896 SetBaseWeaponDamage(BASE_ATTACK
, MINDAMAGE
, float(petlevel
- (petlevel
/ 4)) );
897 //damage range is then petlevel / 2
898 SetBaseWeaponDamage(BASE_ATTACK
, MAXDAMAGE
, float(petlevel
+ (petlevel
/ 4)) );
899 //damage is increased afterwards as strength and pet scaling modify attack power
901 //stored standard pet stats are entry 1 in pet_levelinfo
902 PetLevelInfo
const* pInfo
= objmgr
.GetPetLevelInfo(creature_ID
, petlevel
);
903 if(pInfo
) // exist in DB
905 SetCreateHealth(pInfo
->health
);
906 SetModifierValue(UNIT_MOD_ARMOR
, BASE_VALUE
, float(pInfo
->armor
));
907 //SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, float(cinfo->attackpower));
909 for( int i
= STAT_STRENGTH
; i
< MAX_STATS
; i
++)
911 SetCreateStat(Stats(i
), float(pInfo
->stats
[i
]));
914 else // not exist in DB, use some default fake data
916 sLog
.outErrorDb("Hunter pet levelstats missing in DB");
918 // remove elite bonuses included in DB values
919 SetCreateHealth( uint32(((float(cinfo
->maxhealth
) / cinfo
->maxlevel
) / (1 + 2 * cinfo
->rank
)) * petlevel
) );
921 SetCreateStat(STAT_STRENGTH
, 22);
922 SetCreateStat(STAT_AGILITY
, 22);
923 SetCreateStat(STAT_STAMINA
, 25);
924 SetCreateStat(STAT_INTELLECT
, 28);
925 SetCreateStat(STAT_SPIRIT
, 27);
930 SetUInt32Value(UNIT_FIELD_PETEXPERIENCE
, 0);
931 SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP
, 1000);
933 SetCreateMana(28 + 10*petlevel
);
934 SetCreateHealth(28 + 30*petlevel
);
936 // FIXME: this is wrong formula, possible each guardian pet have own damage formula
937 //these formula may not be correct; however, it is designed to be close to what it should be
938 //this makes dps 0.5 of pets level
939 SetBaseWeaponDamage(BASE_ATTACK
, MINDAMAGE
, float(petlevel
- (petlevel
/ 4)));
940 //damage range is then petlevel / 2
941 SetBaseWeaponDamage(BASE_ATTACK
, MAXDAMAGE
, float(petlevel
+ (petlevel
/ 4)));
944 sLog
.outError("Pet have incorrect type (%u) for levelup.", getPetType());
948 for (int i
= SPELL_SCHOOL_HOLY
; i
< MAX_SPELL_SCHOOL
; ++i
)
949 SetModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START
+ i
), BASE_VALUE
, float(createResistance
[i
]));
953 SetHealth(GetMaxHealth());
954 SetPower(POWER_MANA
, GetMaxPower(POWER_MANA
));
959 bool Pet::HaveInDiet(ItemPrototype
const* item
) const
964 CreatureInfo
const* cInfo
= GetCreatureInfo();
968 CreatureFamilyEntry
const* cFamily
= sCreatureFamilyStore
.LookupEntry(cInfo
->family
);
972 uint32 diet
= cFamily
->petFoodMask
;
973 uint32 FoodMask
= 1 << (item
->FoodType
-1);
974 return diet
& FoodMask
;
977 uint32
Pet::GetCurrentFoodBenefitLevel(uint32 itemlevel
)
979 // -5 or greater food level
980 if(getLevel() <= itemlevel
+ 5) //possible to feed level 60 pet with level 55 level food for full effect
983 else if(getLevel() <= itemlevel
+ 10) //pure guess, but sounds good
986 else if(getLevel() <= itemlevel
+ 14) //level 55 food gets green on 70, makes sense to me
990 return 0; //food too low level
993 void Pet::_LoadSpellCooldowns()
995 m_CreatureSpellCooldowns
.clear();
996 m_CreatureCategoryCooldowns
.clear();
998 QueryResult
*result
= CharacterDatabase
.PQuery("SELECT spell,time FROM pet_spell_cooldown WHERE guid = '%u'",m_charmInfo
->GetPetNumber());
1002 time_t curTime
= time(NULL
);
1004 WorldPacket
data(SMSG_SPELL_COOLDOWN
, (8+1+result
->GetRowCount()*8));
1006 data
<< uint8(0x0); // flags (0x1, 0x2)
1010 Field
*fields
= result
->Fetch();
1012 uint32 spell_id
= fields
[0].GetUInt32();
1013 time_t db_time
= (time_t)fields
[1].GetUInt64();
1015 if(!sSpellStore
.LookupEntry(spell_id
))
1017 sLog
.outError("Pet %u have unknown spell %u in `pet_spell_cooldown`, skipping.",m_charmInfo
->GetPetNumber(),spell_id
);
1021 // skip outdated cooldown
1022 if(db_time
<= curTime
)
1025 data
<< uint32(spell_id
);
1026 data
<< uint32(uint32(db_time
-curTime
)*1000); // in m.secs
1028 _AddCreatureSpellCooldown(spell_id
,db_time
);
1030 sLog
.outDebug("Pet (Number: %u) spell %u cooldown loaded (%u secs).", m_charmInfo
->GetPetNumber(), spell_id
, uint32(db_time
-curTime
));
1032 while( result
->NextRow() );
1036 if(!m_CreatureSpellCooldowns
.empty() && GetOwner())
1038 ((Player
*)GetOwner())->GetSession()->SendPacket(&data
);
1043 void Pet::_SaveSpellCooldowns()
1045 CharacterDatabase
.PExecute("DELETE FROM pet_spell_cooldown WHERE guid = '%u'", m_charmInfo
->GetPetNumber());
1047 time_t curTime
= time(NULL
);
1049 // remove oudated and save active
1050 for(CreatureSpellCooldowns::iterator itr
= m_CreatureSpellCooldowns
.begin();itr
!= m_CreatureSpellCooldowns
.end();)
1052 if(itr
->second
<= curTime
)
1053 m_CreatureSpellCooldowns
.erase(itr
++);
1056 CharacterDatabase
.PExecute("INSERT INTO pet_spell_cooldown (guid,spell,time) VALUES ('%u', '%u', '" I64FMTD
"')", m_charmInfo
->GetPetNumber(), itr
->first
, uint64(itr
->second
));
1062 void Pet::_LoadSpells()
1064 QueryResult
*result
= CharacterDatabase
.PQuery("SELECT spell,active FROM pet_spell WHERE guid = '%u'",m_charmInfo
->GetPetNumber());
1070 Field
*fields
= result
->Fetch();
1072 addSpell(fields
[0].GetUInt32(), fields
[1].GetUInt16(), PETSPELL_UNCHANGED
);
1074 while( result
->NextRow() );
1080 void Pet::_SaveSpells()
1082 for (PetSpellMap::const_iterator itr
= m_spells
.begin(), next
= m_spells
.begin(); itr
!= m_spells
.end(); itr
= next
)
1085 if (itr
->second
->type
== PETSPELL_FAMILY
) continue; // prevent saving family passives to DB
1086 if (itr
->second
->state
== PETSPELL_REMOVED
|| itr
->second
->state
== PETSPELL_CHANGED
)
1087 CharacterDatabase
.PExecute("DELETE FROM pet_spell WHERE guid = '%u' and spell = '%u'", m_charmInfo
->GetPetNumber(), itr
->first
);
1088 if (itr
->second
->state
== PETSPELL_NEW
|| itr
->second
->state
== PETSPELL_CHANGED
)
1089 CharacterDatabase
.PExecute("INSERT INTO pet_spell (guid,spell,active) VALUES ('%u', '%u', '%u')", m_charmInfo
->GetPetNumber(), itr
->first
, itr
->second
->active
);
1091 if (itr
->second
->state
== PETSPELL_REMOVED
)
1092 _removeSpell(itr
->first
);
1094 itr
->second
->state
= PETSPELL_UNCHANGED
;
1098 void Pet::_LoadAuras(uint32 timediff
)
1101 for (int i
= 0; i
< TOTAL_AURAS
; i
++)
1102 m_modAuras
[i
].clear();
1104 QueryResult
*result
= CharacterDatabase
.PQuery("SELECT caster_guid,spell,effect_index,stackcount,amount,maxduration,remaintime,remaincharges FROM pet_aura WHERE guid = '%u'",m_charmInfo
->GetPetNumber());
1110 Field
*fields
= result
->Fetch();
1111 uint64 caster_guid
= fields
[0].GetUInt64();
1112 uint32 spellid
= fields
[1].GetUInt32();
1113 uint32 effindex
= fields
[2].GetUInt32();
1114 uint32 stackcount
= fields
[3].GetUInt32();
1115 int32 damage
= (int32
)fields
[4].GetUInt32();
1116 int32 maxduration
= (int32
)fields
[5].GetUInt32();
1117 int32 remaintime
= (int32
)fields
[6].GetUInt32();
1118 int32 remaincharges
= (int32
)fields
[7].GetUInt32();
1120 SpellEntry
const* spellproto
= sSpellStore
.LookupEntry(spellid
);
1123 sLog
.outError("Unknown aura (spellid %u, effindex %u), ignore.",spellid
,effindex
);
1129 sLog
.outError("Invalid effect index (spellid %u, effindex %u), ignore.",spellid
,effindex
);
1133 // negative effects should continue counting down after logout
1134 if (remaintime
!= -1 && !IsPositiveEffect(spellid
, effindex
))
1136 if(remaintime
<= int32(timediff
))
1139 remaintime
-= timediff
;
1142 // prevent wrong values of remaincharges
1143 if(spellproto
->procCharges
)
1145 if(remaincharges
<= 0 || remaincharges
> spellproto
->procCharges
)
1146 remaincharges
= spellproto
->procCharges
;
1151 /// do not load single target auras (unless they were cast by the player)
1152 if (caster_guid
!= GetGUID() && IsSingleTargetSpell(spellproto
))
1155 for(uint32 i
=0; i
<stackcount
; i
++)
1157 Aura
* aura
= CreateAura(spellproto
, effindex
, NULL
, this, NULL
);
1160 damage
= aura
->GetModifier()->m_amount
;
1161 aura
->SetLoadedState(caster_guid
,damage
,maxduration
,remaintime
,remaincharges
);
1165 while( result
->NextRow() );
1171 void Pet::_SaveAuras()
1173 CharacterDatabase
.PExecute("DELETE FROM pet_aura WHERE guid = '%u'", m_charmInfo
->GetPetNumber());
1175 AuraMap
const& auras
= GetAuras();
1179 spellEffectPair lastEffectPair
= auras
.begin()->first
;
1180 uint32 stackCounter
= 1;
1182 for(AuraMap::const_iterator itr
= auras
.begin(); ; ++itr
)
1184 if(itr
== auras
.end() || lastEffectPair
!= itr
->first
)
1186 AuraMap::const_iterator itr2
= itr
;
1187 // save previous spellEffectPair to db
1189 SpellEntry
const *spellInfo
= itr2
->second
->GetSpellProto();
1190 /// do not save single target auras (unless they were cast by the player)
1191 if (!(itr2
->second
->GetCasterGUID() != GetGUID() && IsSingleTargetSpell(spellInfo
)))
1193 if(!itr2
->second
->IsPassive())
1195 // skip all auras from spell that apply at cast SPELL_AURA_MOD_SHAPESHIFT or pet area auras.
1197 for (i
= 0; i
< 3; i
++)
1198 if (spellInfo
->EffectApplyAuraName
[i
] == SPELL_AURA_MOD_STEALTH
||
1199 spellInfo
->Effect
[i
] == SPELL_EFFECT_APPLY_AREA_AURA_OWNER
||
1200 spellInfo
->Effect
[i
] == SPELL_EFFECT_APPLY_AREA_AURA_PET
)
1205 CharacterDatabase
.PExecute("INSERT INTO pet_aura (guid,caster_guid,spell,effect_index,stackcount,amount,maxduration,remaintime,remaincharges) "
1206 "VALUES ('%u', '" I64FMTD
"', '%u', '%u', '%u', '%d', '%d', '%d', '%d')",
1207 m_charmInfo
->GetPetNumber(), itr2
->second
->GetCasterGUID(),(uint32
)itr2
->second
->GetId(), (uint32
)itr2
->second
->GetEffIndex(), stackCounter
, itr2
->second
->GetModifier()->m_amount
,int(itr2
->second
->GetAuraMaxDuration()),int(itr2
->second
->GetAuraDuration()),int(itr2
->second
->GetAuraCharges()));
1211 if(itr
== auras
.end())
1215 if (lastEffectPair
== itr
->first
)
1219 lastEffectPair
= itr
->first
;
1225 bool Pet::addSpell(uint32 spell_id
, uint16 active
, PetSpellState state
, PetSpellType type
)
1227 SpellEntry
const *spellInfo
= sSpellStore
.LookupEntry(spell_id
);
1230 // do pet spell book cleanup
1231 if(state
== PETSPELL_UNCHANGED
) // spell load case
1233 sLog
.outError("Pet::addSpell: Non-existed in SpellStore spell #%u request, deleting for all pets in `pet_spell`.",spell_id
);
1234 CharacterDatabase
.PExecute("DELETE FROM pet_spell WHERE spell = '%u'",spell_id
);
1237 sLog
.outError("Pet::addSpell: Non-existed in SpellStore spell #%u request.",spell_id
);
1242 PetSpellMap::iterator itr
= m_spells
.find(spell_id
);
1243 if (itr
!= m_spells
.end())
1245 if (itr
->second
->state
== PETSPELL_REMOVED
)
1248 m_spells
.erase(itr
);
1249 state
= PETSPELL_CHANGED
;
1251 else if (state
== PETSPELL_UNCHANGED
&& itr
->second
->state
!= PETSPELL_UNCHANGED
)
1253 // can be in case spell loading but learned at some previous spell loading
1254 itr
->second
->state
= PETSPELL_UNCHANGED
;
1261 uint32 oldspell_id
= 0;
1263 PetSpell
*newspell
= new PetSpell
;
1264 newspell
->state
= state
;
1265 newspell
->type
= type
;
1267 if(active
== ACT_DECIDE
) //active was not used before, so we save it's autocast/passive state here
1269 if(IsPassiveSpell(spell_id
))
1270 newspell
->active
= ACT_PASSIVE
;
1272 newspell
->active
= ACT_DISABLED
;
1275 newspell
->active
= active
;
1277 // talent: unlearn all other talent ranks (high and low)
1278 if(TalentSpellPos
const* talentPos
= GetTalentSpellPos(spell_id
))
1280 if(TalentEntry
const *talentInfo
= sTalentStore
.LookupEntry( talentPos
->talent_id
))
1282 for(int i
=0; i
<5; ++i
)
1284 // skip learning spell and no rank spell case
1285 uint32 rankSpellId
= talentInfo
->RankID
[i
];
1286 if(!rankSpellId
|| rankSpellId
==spell_id
)
1289 // skip unknown ranks
1290 if(!HasSpell(rankSpellId
))
1292 removeSpell(rankSpellId
);
1296 else if(uint32 chainstart
= spellmgr
.GetFirstSpellInChain(spell_id
))
1298 for (PetSpellMap::iterator itr
= m_spells
.begin(); itr
!= m_spells
.end(); ++itr
)
1300 if(itr
->second
->state
== PETSPELL_REMOVED
) continue;
1302 if(spellmgr
.GetFirstSpellInChain(itr
->first
) == chainstart
)
1304 newspell
->active
= itr
->second
->active
;
1306 if(newspell
->active
== ACT_ENABLED
)
1307 ToggleAutocast(itr
->first
, false);
1309 oldspell_id
= itr
->first
;
1310 unlearnSpell(itr
->first
);
1316 m_spells
[spell_id
] = newspell
;
1318 if (IsPassiveSpell(spell_id
))
1319 CastSpell(this, spell_id
, true);
1320 else if(state
== PETSPELL_NEW
)
1321 m_charmInfo
->AddSpellToAB(oldspell_id
, spell_id
);
1323 if(newspell
->active
== ACT_ENABLED
)
1324 ToggleAutocast(spell_id
, true);
1326 uint32 talentCost
= GetTalentSpellCost(spell_id
);
1329 int32 free_points
= GetMaxTalentPointsForLevel(getLevel());
1330 m_usedTalentCount
+=talentCost
;
1331 // update free talent points
1332 free_points
-=m_usedTalentCount
;
1333 SetFreeTalentPoints(free_points
> 0 ? free_points
: 0);
1338 bool Pet::learnSpell(uint32 spell_id
)
1340 // prevent duplicated entires in spell book
1341 if (!addSpell(spell_id
))
1344 Unit
* owner
= GetOwner();
1345 if(owner
&& owner
->GetTypeId() == TYPEID_PLAYER
)
1349 WorldPacket
data(SMSG_PET_LEARNED_SPELL
, 2);
1350 data
<< uint16(spell_id
);
1351 ((Player
*)owner
)->GetSession()->SendPacket(&data
);
1353 ((Player
*)owner
)->PetSpellInitialize();
1358 void Pet::learnLevelupSpells()
1360 PetLevelupSpellSet
const *levelupSpells
= spellmgr
.GetPetLevelupSpellList(GetCreatureInfo()->family
);
1364 uint32 level
= getLevel();
1366 for(PetLevelupSpellSet::const_iterator itr
= levelupSpells
->begin(); itr
!= levelupSpells
->end(); ++itr
)
1368 if(itr
->first
<= level
)
1369 learnSpell(itr
->second
);
1371 unlearnSpell(itr
->second
);
1375 bool Pet::unlearnSpell(uint32 spell_id
)
1377 if(removeSpell(spell_id
))
1379 if(GetOwner()->GetTypeId() == TYPEID_PLAYER
)
1383 WorldPacket
data(SMSG_PET_REMOVED_SPELL
, 2);
1384 data
<< uint16(spell_id
);
1385 ((Player
*)GetOwner())->GetSession()->SendPacket(&data
);
1393 bool Pet::removeSpell(uint32 spell_id
)
1395 PetSpellMap::iterator itr
= m_spells
.find(spell_id
);
1396 if (itr
== m_spells
.end())
1399 if(itr
->second
->state
== PETSPELL_REMOVED
)
1402 if(itr
->second
->state
== PETSPELL_NEW
)
1405 m_spells
.erase(itr
);
1408 itr
->second
->state
= PETSPELL_REMOVED
;
1410 RemoveAurasDueToSpell(spell_id
);
1412 uint32 talentCost
= GetTalentSpellCost(spell_id
);
1415 if (m_usedTalentCount
> talentCost
)
1416 m_usedTalentCount
-=talentCost
;
1418 m_usedTalentCount
= 0;
1419 // update free talent points
1420 int32 free_points
= GetMaxTalentPointsForLevel(getLevel()) - m_usedTalentCount
;
1421 SetFreeTalentPoints(free_points
> 0 ? free_points
: 0);
1427 bool Pet::_removeSpell(uint32 spell_id
)
1429 PetSpellMap::iterator itr
= m_spells
.find(spell_id
);
1430 if (itr
!= m_spells
.end())
1433 m_spells
.erase(itr
);
1439 void Pet::InitPetCreateSpells()
1441 m_charmInfo
->InitPetActionBar();
1443 for (PetSpellMap::iterator i
= m_spells
.begin(); i
!= m_spells
.end(); ++i
)
1448 PetCreateSpellEntry
const* CreateSpells
= objmgr
.GetPetCreateSpellEntry(GetEntry());
1451 for(uint8 i
= 0; i
< 4; i
++)
1453 if(!CreateSpells
->spellid
[i
])
1456 SpellEntry
const *learn_spellproto
= sSpellStore
.LookupEntry(CreateSpells
->spellid
[i
]);
1457 if(!learn_spellproto
)
1460 if(learn_spellproto
->Effect
[0] == SPELL_EFFECT_LEARN_SPELL
|| learn_spellproto
->Effect
[0] == SPELL_EFFECT_LEARN_PET_SPELL
)
1462 petspellid
= learn_spellproto
->EffectTriggerSpell
[0];
1463 Unit
* owner
= GetOwner();
1464 if(owner
->GetTypeId() == TYPEID_PLAYER
&& !((Player
*)owner
)->HasSpell(learn_spellproto
->Id
))
1466 if(IsPassiveSpell(petspellid
)) //learn passive skills when tamed, not sure if thats right
1467 ((Player
*)owner
)->learnSpell(learn_spellproto
->Id
,false);
1469 AddTeachSpell(learn_spellproto
->EffectTriggerSpell
[0], learn_spellproto
->Id
);
1473 petspellid
= learn_spellproto
->Id
;
1475 addSpell(petspellid
);
1481 CastPetAuras(false);
1484 void Pet::CheckLearning(uint32 spellid
)
1486 //charmed case -> prevent crash
1487 if(GetTypeId() == TYPEID_PLAYER
|| getPetType() != HUNTER_PET
)
1490 Unit
* owner
= GetOwner();
1492 if(m_teachspells
.empty() || !owner
|| owner
->GetTypeId() != TYPEID_PLAYER
)
1495 TeachSpellMap::iterator itr
= m_teachspells
.find(spellid
);
1496 if(itr
== m_teachspells
.end())
1499 if(urand(0, 100) < 10)
1501 ((Player
*)owner
)->learnSpell(itr
->second
,false);
1502 m_teachspells
.erase(itr
);
1506 bool Pet::resetTalents(bool no_cost
)
1508 Unit
*owner
= GetOwner();
1509 if (!owner
|| owner
->GetTypeId()!=TYPEID_PLAYER
)
1512 CreatureInfo
const * ci
= GetCreatureInfo();
1515 // Check pet talent type
1516 CreatureFamilyEntry
const *pet_family
= sCreatureFamilyStore
.LookupEntry(ci
->family
);
1517 if(!pet_family
|| pet_family
->petTalentType
< 0)
1520 Player
*player
= (Player
*)owner
;
1522 uint32 level
= getLevel();
1523 uint32 talentPointsForLevel
= GetMaxTalentPointsForLevel(level
);
1525 if (m_usedTalentCount
== 0)
1527 SetFreeTalentPoints(talentPointsForLevel
);
1535 cost
= resetTalentsCost();
1537 if (player
->GetMoney() < cost
)
1539 player
->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY
, 0, 0, 0);
1544 for (unsigned int i
= 0; i
< sTalentStore
.GetNumRows(); i
++)
1546 TalentEntry
const *talentInfo
= sTalentStore
.LookupEntry(i
);
1548 if (!talentInfo
) continue;
1550 TalentTabEntry
const *talentTabInfo
= sTalentTabStore
.LookupEntry( talentInfo
->TalentTab
);
1555 // unlearn only talents for pets family talent type
1556 if(!((1 << pet_family
->petTalentType
) & talentTabInfo
->petTalentMask
))
1559 for (int j
= 0; j
< 5; j
++)
1561 for(PetSpellMap::iterator itr
= m_spells
.begin(); itr
!= m_spells
.end();)
1563 if(itr
->second
->state
== PETSPELL_REMOVED
)
1568 // remove learned spells (all ranks)
1569 uint32 itrFirstId
= spellmgr
.GetFirstSpellInChain(itr
->first
);
1571 // unlearn if first rank is talent or learned by talent
1572 if (itrFirstId
== talentInfo
->RankID
[j
] || spellmgr
.IsSpellLearnToSpell(talentInfo
->RankID
[j
],itrFirstId
))
1574 removeSpell(itr
->first
);
1575 itr
= m_spells
.begin();
1584 SetFreeTalentPoints(talentPointsForLevel
);
1588 player
->ModifyMoney(-(int32
)cost
);
1590 m_resetTalentsCost
= cost
;
1591 m_resetTalentsTime
= time(NULL
);
1593 player
->PetSpellInitialize();
1597 void Pet::InitTalentForLevel()
1599 uint32 level
= getLevel();
1600 uint32 talentPointsForLevel
= GetMaxTalentPointsForLevel(level
);
1601 // Reset talents in case low level (on level down) or wrong points for level (hunter can unlearn TP increase talent)
1602 if(talentPointsForLevel
== 0 || m_usedTalentCount
> talentPointsForLevel
)
1604 // Remove all talent points
1607 SetFreeTalentPoints(talentPointsForLevel
- m_usedTalentCount
);
1610 uint32
Pet::resetTalentsCost() const
1612 uint32 days
= (sWorld
.GetGameTime() - m_resetTalentsTime
)/DAY
;
1614 // The first time reset costs 10 silver; after 1 day cost is reset to 10 silver
1615 if(m_resetTalentsCost
< 10*SILVER
|| days
> 0)
1618 else if(m_resetTalentsCost
< 50*SILVER
)
1621 else if(m_resetTalentsCost
< 1*GOLD
)
1623 // then increasing at a rate of 1 gold; cap 10 gold
1625 return (m_resetTalentsCost
+ 1*GOLD
> 10*GOLD
? 10*GOLD
: m_resetTalentsCost
+ 1*GOLD
);
1628 uint8
Pet::GetMaxTalentPointsForLevel(uint32 level
)
1630 uint8 points
= (level
>= 20) ? ((level
- 16) / 4) : 0;
1631 // Mod points from owner SPELL_AURA_MOD_PET_TALENT_POINTS
1632 if (Unit
*owner
= GetOwner())
1633 points
+=owner
->GetTotalAuraModifier(SPELL_AURA_MOD_PET_TALENT_POINTS
);
1637 void Pet::ToggleAutocast(uint32 spellid
, bool apply
)
1639 if(IsPassiveSpell(spellid
))
1642 PetSpellMap::const_iterator itr
= m_spells
.find(spellid
);
1648 for (i
= 0; i
< m_autospells
.size() && m_autospells
[i
] != spellid
; i
++)
1651 if (i
== m_autospells
.size())
1653 m_autospells
.push_back(spellid
);
1654 itr
->second
->active
= ACT_ENABLED
;
1655 itr
->second
->state
= PETSPELL_CHANGED
;
1660 AutoSpellList::iterator itr2
= m_autospells
.begin();
1661 for (i
= 0; i
< m_autospells
.size() && m_autospells
[i
] != spellid
; i
++, itr2
++)
1664 if (i
< m_autospells
.size())
1666 m_autospells
.erase(itr2
);
1667 itr
->second
->active
= ACT_DISABLED
;
1668 itr
->second
->state
= PETSPELL_CHANGED
;
1673 bool Pet::Create(uint32 guidlow
, Map
*map
, uint32 phaseMask
, uint32 Entry
, uint32 pet_number
)
1675 SetMapId(map
->GetId());
1676 SetInstanceId(map
->GetInstanceId());
1677 SetPhaseMask(phaseMask
,false);
1679 Object::_Create(guidlow
, pet_number
, HIGHGUID_PET
);
1681 m_DBTableGuid
= guidlow
;
1682 m_originalEntry
= Entry
;
1684 if(!InitEntry(Entry
))
1687 SetByteValue(UNIT_FIELD_BYTES_2
, 0, SHEATH_STATE_MELEE
);
1689 if(getPetType() == MINI_PET
) // always non-attackable
1690 SetFlag(UNIT_FIELD_FLAGS
, UNIT_FLAG_NON_ATTACKABLE
);
1695 bool Pet::HasSpell(uint32 spell
) const
1697 PetSpellMap::const_iterator itr
= m_spells
.find(spell
);
1698 return (itr
!= m_spells
.end() && itr
->second
->state
!= PETSPELL_REMOVED
);
1701 // Get all passive spells in our skill line
1702 void Pet::LearnPetPassives()
1704 CreatureInfo
const* cInfo
= GetCreatureInfo();
1708 CreatureFamilyEntry
const* cFamily
= sCreatureFamilyStore
.LookupEntry(cInfo
->family
);
1712 PetFamilySpellsStore::const_iterator petStore
= sPetFamilySpellsStore
.find(cFamily
->ID
);
1713 if(petStore
!= sPetFamilySpellsStore
.end())
1715 for(PetFamilySpellsSet::const_iterator petSet
= petStore
->second
.begin(); petSet
!= petStore
->second
.end(); ++petSet
)
1716 addSpell(*petSet
, ACT_DECIDE
, PETSPELL_NEW
, PETSPELL_FAMILY
);
1720 void Pet::CastPetAuras(bool current
)
1722 Unit
* owner
= GetOwner();
1726 if(getPetType() != HUNTER_PET
&& (getPetType() != SUMMON_PET
|| owner
->getClass() != CLASS_WARLOCK
))
1729 for(PetAuraSet::iterator itr
= owner
->m_petAuras
.begin(); itr
!= owner
->m_petAuras
.end();)
1731 PetAura
const* pa
= *itr
;
1734 if(!current
&& pa
->IsRemovedOnChangePet())
1735 owner
->RemovePetAura(pa
);
1741 void Pet::CastPetAura(PetAura
const* aura
)
1743 uint16 auraId
= aura
->GetAura(GetEntry());
1747 if(auraId
== 35696) // Demonic Knowledge
1749 int32 basePoints
= int32(aura
->GetDamage() * (GetStat(STAT_STAMINA
) + GetStat(STAT_INTELLECT
)) / 100);
1750 CastCustomSpell(this, auraId
, &basePoints
, NULL
, NULL
, true);
1753 CastSpell(this, auraId
, true);