2 * Copyright (C) 2005-2008 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 //numbers represent minutes * 100 while happy (you get 100 loyalty points per min while happy)
43 uint32
const LevelUpLoyalty
[6] =
53 uint32
const LevelStartLoyalty
[6] =
63 Pet::Pet(PetType type
) : Creature()
71 m_happinessTimer
= 7500;
72 m_loyaltyTimer
= 12000;
78 m_resetTalentsCost
= 0;
79 m_resetTalentsTime
= 0;
83 // pets always have a charminfo, even if they are not actually charmed
84 CharmInfo
* charmInfo
= InitCharmInfo(this);
86 if(type
== MINI_PET
) // always passive
87 charmInfo
->SetReactState(REACT_PASSIVE
);
88 else if(type
== GUARDIAN_PET
) // always aggressive
89 charmInfo
->SetReactState(REACT_AGGRESSIVE
);
93 m_CreatureSpellCooldowns
.clear();
94 m_CreatureCategoryCooldowns
.clear();
96 m_declinedname
= NULL
;
101 if(m_uint32Values
) // only for fully created Object
103 for (PetSpellMap::iterator i
= m_spells
.begin(); i
!= m_spells
.end(); ++i
)
105 ObjectAccessor::Instance().RemoveObject(this);
108 delete m_declinedname
;
111 void Pet::AddToWorld()
113 ///- Register the pet for guid lookup
114 if(!IsInWorld()) ObjectAccessor::Instance().AddObject(this);
118 void Pet::RemoveFromWorld()
120 ///- Remove the pet from the accessor
121 if(IsInWorld()) ObjectAccessor::Instance().RemoveObject(this);
122 ///- Don't call the function for Creature, normal mobs + totems go in a different storage
123 Unit::RemoveFromWorld();
126 bool Pet::LoadPetFromDB( Unit
* owner
, uint32 petentry
, uint32 petnumber
, bool current
)
128 uint32 ownerid
= owner
->GetGUIDLow();
133 // known petnumber entry 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
134 result
= CharacterDatabase
.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND id = '%u'",ownerid
, petnumber
);
136 // current pet (slot 0) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
137 result
= CharacterDatabase
.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND slot = '0'",ownerid
);
139 // known petentry entry (unique for summoned pet, but non unique for hunter pet (only from current or not stabled pets)
140 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
141 result
= CharacterDatabase
.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND entry = '%u' AND (slot = '0' OR slot = '3') ",ownerid
, petentry
);
143 // any current or other non-stabled pet (for hunter "call pet")
144 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
145 result
= CharacterDatabase
.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND (slot = '0' OR slot = '3') ",ownerid
);
150 Field
*fields
= result
->Fetch();
152 // update for case of current pet "slot = 0"
153 petentry
= fields
[1].GetUInt32();
160 uint32 summon_spell_id
= fields
[21].GetUInt32();
161 SpellEntry
const* spellInfo
= sSpellStore
.LookupEntry(summon_spell_id
);
163 bool is_temporary_summoned
= spellInfo
&& GetSpellDuration(spellInfo
) > 0;
165 // check temporary summoned pets like mage water elemental
166 if(current
&& is_temporary_summoned
)
172 Map
*map
= owner
->GetMap();
173 uint32 guid
=objmgr
.GenerateLowGuid(HIGHGUID_PET
);
174 uint32 pet_number
= fields
[0].GetUInt32();
175 if(!Create(guid
, map
, petentry
, pet_number
))
182 owner
->GetClosePoint(px
, py
, pz
,GetObjectSize(),PET_FOLLOW_DIST
,PET_FOLLOW_ANGLE
);
184 Relocate(px
, py
, pz
, owner
->GetOrientation());
186 if(!IsPositionValid())
188 sLog
.outError("ERROR: Pet (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)",
189 GetGUIDLow(), GetEntry(), GetPositionX(), GetPositionY());
194 setPetType(PetType(fields
[22].GetUInt8()));
195 SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE
,owner
->getFaction());
196 SetUInt32Value(UNIT_CREATED_BY_SPELL
, summon_spell_id
);
198 CreatureInfo
const *cinfo
= GetCreatureInfo();
199 if(cinfo
->type
== CREATURE_TYPE_CRITTER
)
202 map
->Add((Creature
*)this);
206 if(getPetType()==HUNTER_PET
|| (getPetType()==SUMMON_PET
&& cinfo
->type
== CREATURE_TYPE_DEMON
&& owner
->getClass() == CLASS_WARLOCK
))
207 m_charmInfo
->SetPetNumber(pet_number
, true);
209 m_charmInfo
->SetPetNumber(pet_number
, false);
210 SetUInt64Value(UNIT_FIELD_SUMMONEDBY
, owner
->GetGUID());
211 SetDisplayId(fields
[3].GetUInt32());
212 SetNativeDisplayId(fields
[3].GetUInt32());
213 uint32 petlevel
=fields
[4].GetUInt32();
214 SetUInt32Value(UNIT_NPC_FLAGS
, 0);
215 SetName(fields
[11].GetString());
221 petlevel
=owner
->getLevel();
223 SetUInt32Value(UNIT_FIELD_BYTES_0
,2048);
224 SetUInt32Value(UNIT_FIELD_FLAGS
, UNIT_FLAG_PVP_ATTACKABLE
);
225 // this enables popup window (pet dismiss, cancel)
228 SetUInt32Value(UNIT_FIELD_BYTES_0
, 0x02020100);
229 SetByteValue(UNIT_FIELD_BYTES_1
, 1, fields
[8].GetUInt32());
230 SetByteValue(UNIT_FIELD_BYTES_2
, 0, SHEATH_STATE_MELEE
);
231 SetByteValue(UNIT_FIELD_BYTES_2
, 1, UNIT_BYTE2_FLAG_UNK3
| UNIT_BYTE2_FLAG_AURAS
| UNIT_BYTE2_FLAG_UNK5
);
233 if(fields
[12].GetBool())
234 SetByteValue(UNIT_FIELD_BYTES_2
, 2, UNIT_RENAME_NOT_ALLOWED
);
236 SetByteValue(UNIT_FIELD_BYTES_2
, 2, UNIT_RENAME_ALLOWED
);
238 SetUInt32Value(UNIT_FIELD_FLAGS
, UNIT_FLAG_PVP_ATTACKABLE
);
239 // this enables popup window (pet abandon, cancel)
240 SetTP(fields
[9].GetInt32());
241 SetMaxPower(POWER_HAPPINESS
,GetCreatePowers(POWER_HAPPINESS
));
242 SetPower( POWER_HAPPINESS
,fields
[15].GetUInt32());
243 setPowerType(POWER_FOCUS
);
246 sLog
.outError("Pet have incorrect type (%u) for pet loading.",getPetType());
248 InitStatsForLevel( petlevel
);
249 SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP
, time(NULL
));
250 SetUInt32Value(UNIT_FIELD_PETEXPERIENCE
, fields
[5].GetUInt32());
251 SetUInt64Value(UNIT_FIELD_CREATEDBY
, owner
->GetGUID());
253 m_charmInfo
->SetReactState( ReactStates( fields
[6].GetUInt8() ));
254 m_loyaltyPoints
= fields
[7].GetInt32();
256 uint32 savedhealth
= fields
[13].GetUInt32();
257 uint32 savedmana
= fields
[14].GetUInt32();
259 // set current pet as current
260 if(fields
[10].GetUInt32() != 0)
262 CharacterDatabase
.BeginTransaction();
263 CharacterDatabase
.PExecute("UPDATE character_pet SET slot = '3' WHERE owner = '%u' AND slot = '0' AND id <> '%u'",ownerid
, m_charmInfo
->GetPetNumber());
264 CharacterDatabase
.PExecute("UPDATE character_pet SET slot = '0' WHERE owner = '%u' AND id = '%u'",ownerid
, m_charmInfo
->GetPetNumber());
265 CharacterDatabase
.CommitTransaction();
268 if(!is_temporary_summoned
)
270 // permanent controlled pets store state in DB
271 Tokens tokens
= StrSplit(fields
[16].GetString(), " ");
273 if(tokens
.size() != 20)
280 Tokens::iterator iter
;
281 for(iter
= tokens
.begin(), index
= 0; index
< 10; ++iter
, ++index
)
283 m_charmInfo
->GetActionBarEntry(index
)->Type
= atol((*iter
).c_str());
285 m_charmInfo
->GetActionBarEntry(index
)->SpellOrAction
= atol((*iter
).c_str());
289 tokens
= StrSplit(fields
[17].GetString(), " ");
290 for (iter
= tokens
.begin(), index
= 0; index
< 4; ++iter
, ++index
)
292 uint32 tmp
= atol((*iter
).c_str());
297 AddTeachSpell(tmp
, atol((*iter
).c_str()));
303 // since last save (in seconds)
304 uint32 timediff
= (time(NULL
) - fields
[18].GetUInt32());
308 //load spells/cooldowns/auras
309 SetCanModifyStats(true);
310 _LoadAuras(timediff
);
313 if(is_temporary_summoned
)
315 // Temporary summoned pets always have initial spell list at load
316 InitPetCreateSpells();
321 CastPetAuras(current
);
324 if(getPetType() == SUMMON_PET
&& !current
) //all (?) summon pets come with full health when called, but not when they are current
326 SetHealth(GetMaxHealth());
327 SetPower(POWER_MANA
, GetMaxPower(POWER_MANA
));
331 SetHealth(savedhealth
> GetMaxHealth() ? GetMaxHealth() : savedhealth
);
332 SetPower(POWER_MANA
, savedmana
> GetMaxPower(POWER_MANA
) ? GetMaxPower(POWER_MANA
) : savedmana
);
336 map
->Add((Creature
*)this);
338 // Spells should be loaded after pet is added to map, because in CanCast is check on it
340 _LoadSpellCooldowns();
342 owner
->SetPet(this); // in DB stored only full controlled creature
343 sLog
.outDebug("New Pet has guid %u", GetGUIDLow());
345 if(owner
->GetTypeId() == TYPEID_PLAYER
)
347 ((Player
*)owner
)->PetSpellInitialize();
348 if(((Player
*)owner
)->GetGroup())
349 ((Player
*)owner
)->SetGroupUpdateFlag(GROUP_UPDATE_PET
);
352 if(owner
->GetTypeId() == TYPEID_PLAYER
&& getPetType() == HUNTER_PET
)
354 result
= CharacterDatabase
.PQuery("SELECT genitive, dative, accusative, instrumental, prepositional FROM character_pet_declinedname WHERE owner = '%u' AND id = '%u'",owner
->GetGUIDLow(),GetCharmInfo()->GetPetNumber());
359 delete m_declinedname
;
361 m_declinedname
= new DeclinedName
;
362 Field
*fields
= result
->Fetch();
363 for(int i
= 0; i
< MAX_DECLINED_NAME_CASES
; ++i
)
365 m_declinedname
->name
[i
] = fields
[i
].GetCppString();
373 void Pet::SavePetToDB(PetSaveMode mode
)
378 // save only fully controlled creature
382 uint32 curhealth
= GetHealth();
383 uint32 curmana
= GetPower(POWER_MANA
);
387 case PET_SAVE_IN_STABLE_SLOT_1
:
388 case PET_SAVE_IN_STABLE_SLOT_2
:
389 case PET_SAVE_NOT_IN_SLOT
:
393 //only alive hunter pets get auras saved, the others don't
394 if(!(getPetType() == HUNTER_PET
&& isAlive()))
402 _SaveSpellCooldowns();
407 case PET_SAVE_AS_CURRENT
:
408 case PET_SAVE_IN_STABLE_SLOT_1
:
409 case PET_SAVE_IN_STABLE_SLOT_2
:
410 case PET_SAVE_NOT_IN_SLOT
:
413 if(getPetType()!=HUNTER_PET
)
414 loyalty
= GetLoyaltyLevel();
416 uint32 owner
= GUID_LOPART(GetOwnerGUID());
417 std::string name
= m_name
;
418 CharacterDatabase
.escape_string(name
);
419 CharacterDatabase
.BeginTransaction();
420 // remove current data
421 CharacterDatabase
.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND id = '%u'", owner
,m_charmInfo
->GetPetNumber() );
423 // prevent duplicate using slot (except PET_SAVE_NOT_IN_SLOT)
424 if(mode
!=PET_SAVE_NOT_IN_SLOT
)
425 CharacterDatabase
.PExecute("UPDATE character_pet SET slot = 3 WHERE owner = '%u' AND slot = '%u'", owner
, uint32(mode
) );
427 // prevent existence another hunter pet in PET_SAVE_AS_CURRENT and PET_SAVE_NOT_IN_SLOT
428 if(getPetType()==HUNTER_PET
&& (mode
==PET_SAVE_AS_CURRENT
||mode
==PET_SAVE_NOT_IN_SLOT
))
429 CharacterDatabase
.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND (slot = '0' OR slot = '3')", owner
);
431 std::ostringstream ss
;
432 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) "
434 << m_charmInfo
->GetPetNumber() << ", "
435 << GetEntry() << ", "
437 << GetNativeDisplayId() << ", "
438 << getLevel() << ", "
439 << GetUInt32Value(UNIT_FIELD_PETEXPERIENCE
) << ", "
440 << uint32(m_charmInfo
->GetReactState()) << ", "
441 << m_loyaltyPoints
<< ", "
442 << GetLoyaltyLevel() << ", "
443 << m_TrainingPoints
<< ", "
444 << uint32(mode
) << ", '"
445 << name
.c_str() << "', "
446 << uint32((GetByteValue(UNIT_FIELD_BYTES_2
, 2) == UNIT_RENAME_ALLOWED
)?0:1) << ", "
447 << (curhealth
<1?1:curhealth
) << ", "
449 << GetPower(POWER_HAPPINESS
) << ", '";
451 for(uint32 i
= 0; i
< 10; i
++)
452 ss
<< uint32(m_charmInfo
->GetActionBarEntry(i
)->Type
) << " " << uint32(m_charmInfo
->GetActionBarEntry(i
)->SpellOrAction
) << " ";
455 //save spells the pet can teach to it's Master
458 for(TeachSpellMap::iterator itr
= m_teachspells
.begin(); i
< 4 && itr
!= m_teachspells
.end(); ++i
, ++itr
)
459 ss
<< itr
->first
<< " " << itr
->second
<< " ";
461 ss
<< uint32(0) << " " << uint32(0) << " ";
465 << time(NULL
) << ", "
466 << uint32(m_resetTalentsCost
) << ", "
467 << uint64(m_resetTalentsTime
) << ", "
468 << GetUInt32Value(UNIT_CREATED_BY_SPELL
) << ", "
469 << uint32(getPetType()) << ")";
471 CharacterDatabase
.Execute( ss
.str().c_str() );
473 CharacterDatabase
.CommitTransaction();
476 case PET_SAVE_AS_DELETED
:
479 DeleteFromDB(m_charmInfo
->GetPetNumber());
483 sLog
.outError("Unknown pet save/remove mode: %d",mode
);
487 void Pet::DeleteFromDB(uint32 guidlow
)
489 CharacterDatabase
.PExecute("DELETE FROM character_pet WHERE id = '%u'", guidlow
);
490 CharacterDatabase
.PExecute("DELETE FROM character_pet_declinedname WHERE id = '%u'", guidlow
);
491 CharacterDatabase
.PExecute("DELETE FROM pet_aura WHERE guid = '%u'", guidlow
);
492 CharacterDatabase
.PExecute("DELETE FROM pet_spell WHERE guid = '%u'", guidlow
);
493 CharacterDatabase
.PExecute("DELETE FROM pet_spell_cooldown WHERE guid = '%u'", guidlow
);
496 void Pet::setDeathState(DeathState s
) // overwrite virtual Creature::setDeathState and Unit::setDeathState
498 Creature::setDeathState(s
);
499 if(getDeathState()==CORPSE
)
501 //remove summoned pet (no corpse)
502 if(getPetType()==SUMMON_PET
)
503 Remove(PET_SAVE_NOT_IN_SLOT
);
504 // other will despawn at corpse desppawning (Pet::Update code)
507 // pet corpse non lootable and non skinnable
508 SetUInt32Value( UNIT_DYNAMIC_FLAGS
, 0x00 );
509 RemoveFlag (UNIT_FIELD_FLAGS
, UNIT_FLAG_SKINNABLE
);
511 //lose happiness when died and not in BG/Arena
512 MapEntry
const* mapEntry
= sMapStore
.LookupEntry(GetMapId());
513 if(!mapEntry
|| (mapEntry
->map_type
!= MAP_ARENA
&& mapEntry
->map_type
!= MAP_BATTLEGROUND
))
514 ModifyPower(POWER_HAPPINESS
, -HAPPINESS_LEVEL_SIZE
);
516 SetFlag(UNIT_FIELD_FLAGS
, UNIT_FLAG_DISABLE_ROTATE
);
519 else if(getDeathState()==ALIVE
)
521 RemoveFlag(UNIT_FIELD_FLAGS
, UNIT_FLAG_DISABLE_ROTATE
);
526 void Pet::Update(uint32 diff
)
528 if(m_removed
) // pet already removed, just wait in remove queue, no updates
531 switch( m_deathState
)
535 if( m_deathTimer
<= diff
)
537 assert(getPetType()!=SUMMON_PET
&& "Must be already removed.");
538 Remove(PET_SAVE_NOT_IN_SLOT
); //hunters' pets never get removed because of death, NEVER!
545 // unsummon pet that lost owner
546 Unit
* owner
= GetOwner();
547 if(!owner
|| !IsWithinDistInMap(owner
, OWNER_MAX_DISTANCE
) || isControlled() && !owner
->GetPetGUID())
549 Remove(PET_SAVE_NOT_IN_SLOT
, true);
555 if( owner
->GetPetGUID() != GetGUID() )
557 Remove(getPetType()==HUNTER_PET
?PET_SAVE_AS_DELETED
:PET_SAVE_NOT_IN_SLOT
);
564 if(m_duration
> diff
)
568 Remove(getPetType() != SUMMON_PET
? PET_SAVE_AS_DELETED
:PET_SAVE_NOT_IN_SLOT
);
573 if(getPetType() != HUNTER_PET
)
577 if(m_regenTimer
<= diff
)
583 m_regenTimer
-= diff
;
585 if(m_happinessTimer
<= diff
)
588 m_happinessTimer
= 7500;
591 m_happinessTimer
-= diff
;
593 if(m_loyaltyTimer
<= diff
)
596 m_loyaltyTimer
= 12000;
599 m_loyaltyTimer
-= diff
;
606 Creature::Update(diff
);
609 void Pet::RegenerateFocus()
611 uint32 curValue
= GetPower(POWER_FOCUS
);
612 uint32 maxValue
= GetMaxPower(POWER_FOCUS
);
614 if (curValue
>= maxValue
)
617 float addvalue
= 24 * sWorld
.getRate(RATE_POWER_FOCUS
);
619 AuraList
const& ModPowerRegenPCTAuras
= GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT
);
620 for(AuraList::const_iterator i
= ModPowerRegenPCTAuras
.begin(); i
!= ModPowerRegenPCTAuras
.end(); ++i
)
621 if ((*i
)->GetModifier()->m_miscvalue
== POWER_FOCUS
)
622 addvalue
*= ((*i
)->GetModifier()->m_amount
+ 100) / 100.0f
;
624 ModifyPower(POWER_FOCUS
, (int32
)addvalue
);
627 void Pet::LooseHappiness()
629 uint32 curValue
= GetPower(POWER_HAPPINESS
);
632 int32 addvalue
= (140 >> GetLoyaltyLevel()) * 125; //value is 70/35/17/8/4 (per min) * 1000 / 8 (timer 7.5 secs)
633 if(isInCombat()) //we know in combat happiness fades faster, multiplier guess
634 addvalue
= int32(addvalue
* 1.5);
635 ModifyPower(POWER_HAPPINESS
, -addvalue
);
638 void Pet::ModifyLoyalty(int32 addvalue
)
640 uint32 loyaltylevel
= GetLoyaltyLevel();
642 if(addvalue
> 0) //only gain influenced, not loss
643 addvalue
= int32((float)addvalue
* sWorld
.getRate(RATE_LOYALTY
));
645 if(loyaltylevel
>= BEST_FRIEND
&& (addvalue
+ m_loyaltyPoints
) > int32(GetMaxLoyaltyPoints(loyaltylevel
)))
648 m_loyaltyPoints
+= addvalue
;
650 if(m_loyaltyPoints
< 0)
652 if(loyaltylevel
> REBELLIOUS
)
656 SetLoyaltyLevel(LoyaltyLevel(loyaltylevel
));
657 m_loyaltyPoints
= GetStartLoyaltyPoints(loyaltylevel
);
658 SetTP(m_TrainingPoints
- int32(getLevel()));
663 Unit
* owner
= GetOwner();
664 if(owner
&& owner
->GetTypeId() == TYPEID_PLAYER
)
666 WorldPacket
data(SMSG_PET_BROKEN
, 0);
667 ((Player
*)owner
)->GetSession()->SendPacket(&data
);
670 ((Player
*)owner
)->RemovePet(this,PET_SAVE_AS_DELETED
);
675 else if(m_loyaltyPoints
> int32(GetMaxLoyaltyPoints(loyaltylevel
)))
678 SetLoyaltyLevel(LoyaltyLevel(loyaltylevel
));
679 m_loyaltyPoints
= GetStartLoyaltyPoints(loyaltylevel
);
680 SetTP(m_TrainingPoints
+ getLevel());
684 void Pet::TickLoyaltyChange()
688 switch(GetHappinessState())
690 case HAPPY
: addvalue
= 20; break;
691 case CONTENT
: addvalue
= 10; break;
692 case UNHAPPY
: addvalue
= -20; break;
696 ModifyLoyalty(addvalue
);
699 void Pet::KillLoyaltyBonus(uint32 level
)
704 //at lower levels gain is faster | the lower loyalty the more loyalty is gained
705 uint32 bonus
= uint32(((100 - level
) / 10) + (6 - GetLoyaltyLevel()));
706 ModifyLoyalty(bonus
);
709 HappinessState
Pet::GetHappinessState()
711 if(GetPower(POWER_HAPPINESS
) < HAPPINESS_LEVEL_SIZE
)
713 else if(GetPower(POWER_HAPPINESS
) >= HAPPINESS_LEVEL_SIZE
* 2)
719 void Pet::SetLoyaltyLevel(LoyaltyLevel level
)
721 SetByteValue(UNIT_FIELD_BYTES_1
, 1, level
);
724 bool Pet::CanTakeMoreActiveSpells(uint32 spellid
)
726 uint8 activecount
= 1;
727 uint32 chainstartstore
[ACTIVE_SPELLS_MAX
];
729 if(IsPassiveSpell(spellid
))
732 chainstartstore
[0] = spellmgr
.GetFirstSpellInChain(spellid
);
734 for (PetSpellMap::iterator itr
= m_spells
.begin(); itr
!= m_spells
.end(); ++itr
)
736 if(IsPassiveSpell(itr
->first
))
739 uint32 chainstart
= spellmgr
.GetFirstSpellInChain(itr
->first
);
743 for(x
= 0; x
< activecount
; x
++)
745 if(chainstart
== chainstartstore
[x
])
749 if(x
== activecount
) //spellchain not yet saved -> add active count
752 if(activecount
> ACTIVE_SPELLS_MAX
)
754 chainstartstore
[x
] = chainstart
;
760 bool Pet::HasTPForSpell(uint32 spellid
)
762 int32 neededtrainp
= GetTPForSpell(spellid
);
763 if((m_TrainingPoints
- neededtrainp
< 0 || neededtrainp
< 0) && neededtrainp
!= 0)
768 int32
Pet::GetTPForSpell(uint32 spellid
)
770 uint32 basetrainp
= 0;
772 SkillLineAbilityMap::const_iterator lower
= spellmgr
.GetBeginSkillLineAbilityMap(spellid
);
773 SkillLineAbilityMap::const_iterator upper
= spellmgr
.GetEndSkillLineAbilityMap(spellid
);
774 for(SkillLineAbilityMap::const_iterator _spell_idx
= lower
; _spell_idx
!= upper
; ++_spell_idx
)
776 if(!_spell_idx
->second
->reqtrainpoints
)
779 basetrainp
= _spell_idx
->second
->reqtrainpoints
;
783 uint32 spenttrainp
= 0;
784 uint32 chainstart
= spellmgr
.GetFirstSpellInChain(spellid
);
786 for (PetSpellMap::iterator itr
= m_spells
.begin(); itr
!= m_spells
.end(); ++itr
)
788 if(itr
->second
->state
== PETSPELL_REMOVED
)
791 if(spellmgr
.GetFirstSpellInChain(itr
->first
) == chainstart
)
793 SkillLineAbilityMap::const_iterator _lower
= spellmgr
.GetBeginSkillLineAbilityMap(itr
->first
);
794 SkillLineAbilityMap::const_iterator _upper
= spellmgr
.GetEndSkillLineAbilityMap(itr
->first
);
796 for(SkillLineAbilityMap::const_iterator _spell_idx2
= _lower
; _spell_idx2
!= _upper
; ++_spell_idx2
)
798 if(_spell_idx2
->second
->reqtrainpoints
> spenttrainp
)
800 spenttrainp
= _spell_idx2
->second
->reqtrainpoints
;
807 return int32(basetrainp
) - int32(spenttrainp
);
810 uint32
Pet::GetMaxLoyaltyPoints(uint32 level
)
812 return LevelUpLoyalty
[level
- 1];
815 uint32
Pet::GetStartLoyaltyPoints(uint32 level
)
817 return LevelStartLoyalty
[level
- 1];
820 void Pet::SetTP(int32 TP
)
822 m_TrainingPoints
= TP
;
823 SetUInt32Value(UNIT_TRAINING_POINTS
, (uint32
)GetDispTP());
826 int32
Pet::GetDispTP()
828 if(getPetType()!= HUNTER_PET
)
830 if(m_TrainingPoints
< 0)
831 return -m_TrainingPoints
;
833 return -(m_TrainingPoints
+ 1);
836 void Pet::Remove(PetSaveMode mode
, bool returnreagent
)
838 Unit
* owner
= GetOwner();
842 if(owner
->GetTypeId()==TYPEID_PLAYER
)
844 ((Player
*)owner
)->RemovePet(this,mode
,returnreagent
);
848 // only if current pet in slot
849 if(owner
->GetPetGUID()==GetGUID())
853 CleanupsBeforeDelete();
854 AddObjectToRemoveList();
858 void Pet::GivePetXP(uint32 xp
)
860 if(getPetType() != HUNTER_PET
)
869 uint32 level
= getLevel();
871 // XP to money conversion processed in Player::RewardQuest
872 if(level
>= sWorld
.getConfig(CONFIG_MAX_PLAYER_LEVEL
))
875 uint32 curXP
= GetUInt32Value(UNIT_FIELD_PETEXPERIENCE
);
876 uint32 nextLvlXP
= GetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP
);
877 uint32 newXP
= curXP
+ xp
;
879 if(newXP
>= nextLvlXP
&& level
+1 > GetOwner()->getLevel())
881 SetUInt32Value(UNIT_FIELD_PETEXPERIENCE
, nextLvlXP
-1);
885 while( newXP
>= nextLvlXP
&& level
< sWorld
.getConfig(CONFIG_MAX_PLAYER_LEVEL
) )
889 SetLevel( level
+ 1 );
890 SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP
, uint32((MaNGOS::XP::xp_to_level(level
+1))/4));
893 nextLvlXP
= GetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP
);
897 SetUInt32Value(UNIT_FIELD_PETEXPERIENCE
, newXP
);
899 if(getPetType() == HUNTER_PET
)
900 KillLoyaltyBonus(level
);
903 void Pet::GivePetLevel(uint32 level
)
908 InitStatsForLevel( level
);
910 SetTP(m_TrainingPoints
+ (GetLoyaltyLevel() - 1));
913 bool Pet::CreateBaseAtCreature(Creature
* creature
)
917 sLog
.outError("CRITICAL ERROR: NULL pointer parsed into CreateBaseAtCreature()");
920 uint32 guid
=objmgr
.GenerateLowGuid(HIGHGUID_PET
);
922 sLog
.outBasic("SetInstanceID()");
923 SetInstanceId(creature
->GetInstanceId());
925 sLog
.outBasic("Create pet");
926 uint32 pet_number
= objmgr
.GeneratePetNumber();
927 if(!Create(guid
, creature
->GetMap(), creature
->GetEntry(), pet_number
))
930 Relocate(creature
->GetPositionX(), creature
->GetPositionY(), creature
->GetPositionZ(), creature
->GetOrientation());
932 if(!IsPositionValid())
934 sLog
.outError("ERROR: Pet (guidlow %d, entry %d) not created base at creature. Suggested coordinates isn't valid (X: %f Y: %f)",
935 GetGUIDLow(), GetEntry(), GetPositionX(), GetPositionY());
939 CreatureInfo
const *cinfo
= GetCreatureInfo();
942 sLog
.outError("ERROR: CreateBaseAtCreature() failed, creatureInfo is missing!");
946 if(cinfo
->type
== CREATURE_TYPE_CRITTER
)
948 setPetType(MINI_PET
);
951 SetDisplayId(creature
->GetDisplayId());
952 SetNativeDisplayId(creature
->GetNativeDisplayId());
953 SetMaxPower(POWER_HAPPINESS
,GetCreatePowers(POWER_HAPPINESS
));
954 SetPower( POWER_HAPPINESS
,166500);
955 setPowerType(POWER_FOCUS
);
956 SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP
,0);
957 SetUInt32Value(UNIT_FIELD_PETEXPERIENCE
,0);
958 SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP
, uint32((MaNGOS::XP::xp_to_level(creature
->getLevel()))/4));
959 SetUInt32Value(UNIT_FIELD_FLAGS
, UNIT_FLAG_PVP_ATTACKABLE
);
960 SetUInt32Value(UNIT_NPC_FLAGS
, 0);
962 CreatureFamilyEntry
const* cFamily
= sCreatureFamilyStore
.LookupEntry(creature
->GetCreatureInfo()->family
);
963 if( char* familyname
= cFamily
->Name
[sWorld
.GetDefaultDbcLocale()] )
966 SetName(creature
->GetName());
968 m_loyaltyPoints
= 1000;
969 if(cinfo
->type
== CREATURE_TYPE_BEAST
)
971 SetUInt32Value(UNIT_FIELD_BYTES_0
, 0x02020100);
972 SetByteValue(UNIT_FIELD_BYTES_2
, 0, SHEATH_STATE_MELEE
);
973 SetByteValue(UNIT_FIELD_BYTES_2
, 1, UNIT_BYTE2_FLAG_UNK3
| UNIT_BYTE2_FLAG_AURAS
| UNIT_BYTE2_FLAG_UNK5
);
974 SetByteValue(UNIT_FIELD_BYTES_2
, 2, UNIT_RENAME_ALLOWED
);
976 SetUInt32Value(UNIT_MOD_CAST_SPEED
, creature
->GetUInt32Value(UNIT_MOD_CAST_SPEED
) );
977 SetLoyaltyLevel(REBELLIOUS
);
982 bool Pet::InitStatsForLevel(uint32 petlevel
)
984 CreatureInfo
const *cinfo
= GetCreatureInfo();
987 Unit
* owner
= GetOwner();
990 sLog
.outError("ERROR: attempt to summon pet (Entry %u) without owner! Attempt terminated.", cinfo
->Entry
);
994 uint32 creature_ID
= (getPetType() == HUNTER_PET
) ? 1 : cinfo
->Entry
;
998 SetMeleeDamageSchool(SpellSchools(cinfo
->dmgschool
));
1000 SetModifierValue(UNIT_MOD_ARMOR
, BASE_VALUE
, float(petlevel
*50));
1002 SetAttackTime(BASE_ATTACK
, BASE_ATTACK_TIME
);
1003 SetAttackTime(OFF_ATTACK
, BASE_ATTACK_TIME
);
1004 SetAttackTime(RANGED_ATTACK
, BASE_ATTACK_TIME
);
1006 SetFloatValue(UNIT_MOD_CAST_SPEED
, 1.0);
1008 CreatureFamilyEntry
const* cFamily
= sCreatureFamilyStore
.LookupEntry(cinfo
->family
);
1009 if(cFamily
&& cFamily
->minScale
> 0.0f
)
1012 if (getLevel() >= cFamily
->maxScaleLevel
)
1013 scale
= cFamily
->maxScale
;
1014 else if (getLevel() <= cFamily
->minScaleLevel
)
1015 scale
= cFamily
->minScale
;
1017 scale
= cFamily
->minScale
+ (getLevel() - cFamily
->minScaleLevel
) / cFamily
->maxScaleLevel
* (cFamily
->maxScale
- cFamily
->minScale
);
1019 SetFloatValue(OBJECT_FIELD_SCALE_X
, scale
);
1023 int32 createResistance
[MAX_SPELL_SCHOOL
] = {0,0,0,0,0,0,0};
1025 if(cinfo
&& getPetType() != HUNTER_PET
)
1027 createResistance
[SPELL_SCHOOL_HOLY
] = cinfo
->resistance1
;
1028 createResistance
[SPELL_SCHOOL_FIRE
] = cinfo
->resistance2
;
1029 createResistance
[SPELL_SCHOOL_NATURE
] = cinfo
->resistance3
;
1030 createResistance
[SPELL_SCHOOL_FROST
] = cinfo
->resistance4
;
1031 createResistance
[SPELL_SCHOOL_SHADOW
] = cinfo
->resistance5
;
1032 createResistance
[SPELL_SCHOOL_ARCANE
] = cinfo
->resistance6
;
1035 switch(getPetType())
1039 if(owner
->GetTypeId() == TYPEID_PLAYER
)
1041 switch(owner
->getClass())
1046 //the damage bonus used for pets is either fire or shadow damage, whatever is higher
1047 uint32 fire
= owner
->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS
+ SPELL_SCHOOL_FIRE
);
1048 uint32 shadow
= owner
->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS
+ SPELL_SCHOOL_SHADOW
);
1049 uint32 val
= (fire
> shadow
) ? fire
: shadow
;
1051 SetBonusDamage(int32 (val
* 0.15f
));
1052 //bonusAP += val * 0.57;
1057 //40% damage bonus of mage's frost damage
1058 float val
= owner
->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS
+ SPELL_SCHOOL_FROST
) * 0.4;
1061 SetBonusDamage( int32(val
));
1069 SetBaseWeaponDamage(BASE_ATTACK
, MINDAMAGE
, float(petlevel
- (petlevel
/ 4)) );
1070 SetBaseWeaponDamage(BASE_ATTACK
, MAXDAMAGE
, float(petlevel
+ (petlevel
/ 4)) );
1072 //SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, float(cinfo->attackpower));
1074 PetLevelInfo
const* pInfo
= objmgr
.GetPetLevelInfo(creature_ID
, petlevel
);
1075 if(pInfo
) // exist in DB
1077 SetCreateHealth(pInfo
->health
);
1078 SetCreateMana(pInfo
->mana
);
1080 if(pInfo
->armor
> 0)
1081 SetModifierValue(UNIT_MOD_ARMOR
, BASE_VALUE
, float(pInfo
->armor
));
1083 for(int stat
= 0; stat
< MAX_STATS
; ++stat
)
1085 SetCreateStat(Stats(stat
),float(pInfo
->stats
[stat
]));
1088 else // not exist in DB, use some default fake data
1090 sLog
.outErrorDb("Summoned pet (Entry: %u) not have pet stats data in DB",cinfo
->Entry
);
1092 // remove elite bonuses included in DB values
1093 SetCreateHealth(uint32(((float(cinfo
->maxhealth
) / cinfo
->maxlevel
) / (1 + 2 * cinfo
->rank
)) * petlevel
) );
1094 SetCreateMana( uint32(((float(cinfo
->maxmana
) / cinfo
->maxlevel
) / (1 + 2 * cinfo
->rank
)) * petlevel
) );
1096 SetCreateStat(STAT_STRENGTH
,22);
1097 SetCreateStat(STAT_AGILITY
,22);
1098 SetCreateStat(STAT_STAMINA
,25);
1099 SetCreateStat(STAT_INTELLECT
,28);
1100 SetCreateStat(STAT_SPIRIT
,27);
1106 SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP
, uint32((MaNGOS::XP::xp_to_level(petlevel
))/4));
1108 //these formula may not be correct; however, it is designed to be close to what it should be
1109 //this makes dps 0.5 of pets level
1110 SetBaseWeaponDamage(BASE_ATTACK
, MINDAMAGE
, float(petlevel
- (petlevel
/ 4)) );
1111 //damage range is then petlevel / 2
1112 SetBaseWeaponDamage(BASE_ATTACK
, MAXDAMAGE
, float(petlevel
+ (petlevel
/ 4)) );
1113 //damage is increased afterwards as strength and pet scaling modify attack power
1115 //stored standard pet stats are entry 1 in pet_levelinfo
1116 PetLevelInfo
const* pInfo
= objmgr
.GetPetLevelInfo(creature_ID
, petlevel
);
1117 if(pInfo
) // exist in DB
1119 SetCreateHealth(pInfo
->health
);
1120 SetModifierValue(UNIT_MOD_ARMOR
, BASE_VALUE
, float(pInfo
->armor
));
1121 //SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, float(cinfo->attackpower));
1123 for( int i
= STAT_STRENGTH
; i
< MAX_STATS
; i
++)
1125 SetCreateStat(Stats(i
), float(pInfo
->stats
[i
]));
1128 else // not exist in DB, use some default fake data
1130 sLog
.outErrorDb("Hunter pet levelstats missing in DB");
1132 // remove elite bonuses included in DB values
1133 SetCreateHealth( uint32(((float(cinfo
->maxhealth
) / cinfo
->maxlevel
) / (1 + 2 * cinfo
->rank
)) * petlevel
) );
1135 SetCreateStat(STAT_STRENGTH
,22);
1136 SetCreateStat(STAT_AGILITY
,22);
1137 SetCreateStat(STAT_STAMINA
,25);
1138 SetCreateStat(STAT_INTELLECT
,28);
1139 SetCreateStat(STAT_SPIRIT
,27);
1144 SetUInt32Value(UNIT_FIELD_PETEXPERIENCE
,0);
1145 SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP
,1000);
1147 SetCreateMana( 28 + 10*petlevel
);
1148 SetCreateHealth( 28 + 30*petlevel
);
1150 // FIXME: this is wrong formula, possible each guardian pet have own damage formula
1151 //these formula may not be correct; however, it is designed to be close to what it should be
1152 //this makes dps 0.5 of pets level
1153 SetBaseWeaponDamage(BASE_ATTACK
, MINDAMAGE
, float(petlevel
- (petlevel
/ 4)) );
1154 //damage range is then petlevel / 2
1155 SetBaseWeaponDamage(BASE_ATTACK
, MAXDAMAGE
, float(petlevel
+ (petlevel
/ 4)) );
1158 sLog
.outError("Pet have incorrect type (%u) for levelup.",getPetType()); break;
1161 for (int i
= SPELL_SCHOOL_HOLY
; i
< MAX_SPELL_SCHOOL
; ++i
)
1162 SetModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START
+ i
), BASE_VALUE
, float(createResistance
[i
]) );
1166 SetHealth(GetMaxHealth());
1167 SetPower(POWER_MANA
, GetMaxPower(POWER_MANA
));
1172 bool Pet::HaveInDiet(ItemPrototype
const* item
) const
1174 if (!item
->FoodType
)
1177 CreatureInfo
const* cInfo
= GetCreatureInfo();
1181 CreatureFamilyEntry
const* cFamily
= sCreatureFamilyStore
.LookupEntry(cInfo
->family
);
1185 uint32 diet
= cFamily
->petFoodMask
;
1186 uint32 FoodMask
= 1 << (item
->FoodType
-1);
1187 return diet
& FoodMask
;
1190 uint32
Pet::GetCurrentFoodBenefitLevel(uint32 itemlevel
)
1192 // -5 or greater food level
1193 if(getLevel() <= itemlevel
+5) //possible to feed level 60 pet with level 55 level food for full effect
1196 else if(getLevel() <= itemlevel
+ 10) //pure guess, but sounds good
1199 else if(getLevel() <= itemlevel
+ 14) //level 55 food gets green on 70, makes sense to me
1203 return 0; //food too low level
1206 void Pet::_LoadSpellCooldowns()
1208 m_CreatureSpellCooldowns
.clear();
1209 m_CreatureCategoryCooldowns
.clear();
1211 QueryResult
*result
= CharacterDatabase
.PQuery("SELECT spell,time FROM pet_spell_cooldown WHERE guid = '%u'",m_charmInfo
->GetPetNumber());
1215 time_t curTime
= time(NULL
);
1217 WorldPacket
data(SMSG_SPELL_COOLDOWN
, (8+1+result
->GetRowCount()*8));
1219 data
<< uint8(0x0); // flags (0x1, 0x2)
1223 Field
*fields
= result
->Fetch();
1225 uint32 spell_id
= fields
[0].GetUInt32();
1226 time_t db_time
= (time_t)fields
[1].GetUInt64();
1228 if(!sSpellStore
.LookupEntry(spell_id
))
1230 sLog
.outError("Pet %u have unknown spell %u in `pet_spell_cooldown`, skipping.",m_charmInfo
->GetPetNumber(),spell_id
);
1234 // skip outdated cooldown
1235 if(db_time
<= curTime
)
1238 data
<< uint32(spell_id
);
1239 data
<< uint32(uint32(db_time
-curTime
)*1000); // in m.secs
1241 _AddCreatureSpellCooldown(spell_id
,db_time
);
1243 sLog
.outDebug("Pet (Number: %u) spell %u cooldown loaded (%u secs).",m_charmInfo
->GetPetNumber(),spell_id
,uint32(db_time
-curTime
));
1245 while( result
->NextRow() );
1249 if(!m_CreatureSpellCooldowns
.empty() && GetOwner())
1251 ((Player
*)GetOwner())->GetSession()->SendPacket(&data
);
1256 void Pet::_SaveSpellCooldowns()
1258 CharacterDatabase
.PExecute("DELETE FROM pet_spell_cooldown WHERE guid = '%u'", m_charmInfo
->GetPetNumber());
1260 time_t curTime
= time(NULL
);
1262 // remove oudated and save active
1263 for(CreatureSpellCooldowns::iterator itr
= m_CreatureSpellCooldowns
.begin();itr
!= m_CreatureSpellCooldowns
.end();)
1265 if(itr
->second
<= curTime
)
1266 m_CreatureSpellCooldowns
.erase(itr
++);
1269 CharacterDatabase
.PExecute("INSERT INTO pet_spell_cooldown (guid,spell,time) VALUES ('%u', '%u', '" I64FMTD
"')", m_charmInfo
->GetPetNumber(), itr
->first
, uint64(itr
->second
));
1275 void Pet::_LoadSpells()
1277 QueryResult
*result
= CharacterDatabase
.PQuery("SELECT spell,slot,active FROM pet_spell WHERE guid = '%u'",m_charmInfo
->GetPetNumber());
1283 Field
*fields
= result
->Fetch();
1285 addSpell(fields
[0].GetUInt16(), fields
[2].GetUInt16(), PETSPELL_UNCHANGED
, fields
[1].GetUInt16());
1287 while( result
->NextRow() );
1293 void Pet::_SaveSpells()
1295 for (PetSpellMap::const_iterator itr
= m_spells
.begin(), next
= m_spells
.begin(); itr
!= m_spells
.end(); itr
= next
)
1298 if (itr
->second
->type
== PETSPELL_FAMILY
) continue; // prevent saving family passives to DB
1299 if (itr
->second
->state
== PETSPELL_REMOVED
|| itr
->second
->state
== PETSPELL_CHANGED
)
1300 CharacterDatabase
.PExecute("DELETE FROM pet_spell WHERE guid = '%u' and spell = '%u'", m_charmInfo
->GetPetNumber(), itr
->first
);
1301 if (itr
->second
->state
== PETSPELL_NEW
|| itr
->second
->state
== PETSPELL_CHANGED
)
1302 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
);
1304 if (itr
->second
->state
== PETSPELL_REMOVED
)
1305 _removeSpell(itr
->first
);
1307 itr
->second
->state
= PETSPELL_UNCHANGED
;
1311 void Pet::_LoadAuras(uint32 timediff
)
1314 for (int i
= 0; i
< TOTAL_AURAS
; i
++)
1315 m_modAuras
[i
].clear();
1317 // all aura related fields
1318 for(int i
= UNIT_FIELD_AURA
; i
<= UNIT_FIELD_AURASTATE
; ++i
)
1319 SetUInt32Value(i
, 0);
1321 QueryResult
*result
= CharacterDatabase
.PQuery("SELECT caster_guid,spell,effect_index,stackcount,amount,maxduration,remaintime,remaincharges FROM pet_aura WHERE guid = '%u'",m_charmInfo
->GetPetNumber());
1327 Field
*fields
= result
->Fetch();
1328 uint64 caster_guid
= fields
[0].GetUInt64();
1329 uint32 spellid
= fields
[1].GetUInt32();
1330 uint32 effindex
= fields
[2].GetUInt32();
1331 uint32 stackcount
= fields
[3].GetUInt32();
1332 int32 damage
= (int32
)fields
[4].GetUInt32();
1333 int32 maxduration
= (int32
)fields
[5].GetUInt32();
1334 int32 remaintime
= (int32
)fields
[6].GetUInt32();
1335 int32 remaincharges
= (int32
)fields
[7].GetUInt32();
1337 SpellEntry
const* spellproto
= sSpellStore
.LookupEntry(spellid
);
1340 sLog
.outError("Unknown aura (spellid %u, effindex %u), ignore.",spellid
,effindex
);
1346 sLog
.outError("Invalid effect index (spellid %u, effindex %u), ignore.",spellid
,effindex
);
1350 // negative effects should continue counting down after logout
1351 if (remaintime
!= -1 && !IsPositiveEffect(spellid
, effindex
))
1353 if(remaintime
<= int32(timediff
))
1356 remaintime
-= timediff
;
1359 // prevent wrong values of remaincharges
1360 if(spellproto
->procCharges
)
1362 if(remaincharges
<= 0 || remaincharges
> spellproto
->procCharges
)
1363 remaincharges
= spellproto
->procCharges
;
1368 /// do not load single target auras (unless they were cast by the player)
1369 if (caster_guid
!= GetGUID() && IsSingleTargetSpell(spellproto
))
1372 for(uint32 i
=0; i
<stackcount
; i
++)
1374 Aura
* aura
= CreateAura(spellproto
, effindex
, NULL
, this, NULL
);
1377 damage
= aura
->GetModifier()->m_amount
;
1378 aura
->SetLoadedState(caster_guid
,damage
,maxduration
,remaintime
,remaincharges
);
1382 while( result
->NextRow() );
1388 void Pet::_SaveAuras()
1390 CharacterDatabase
.PExecute("DELETE FROM pet_aura WHERE guid = '%u'",m_charmInfo
->GetPetNumber());
1392 AuraMap
const& auras
= GetAuras();
1396 spellEffectPair lastEffectPair
= auras
.begin()->first
;
1397 uint32 stackCounter
= 1;
1399 for(AuraMap::const_iterator itr
= auras
.begin(); ; ++itr
)
1401 if(itr
== auras
.end() || lastEffectPair
!= itr
->first
)
1403 AuraMap::const_iterator itr2
= itr
;
1404 // save previous spellEffectPair to db
1406 SpellEntry
const *spellInfo
= itr2
->second
->GetSpellProto();
1407 /// do not save single target auras (unless they were cast by the player)
1408 if (!(itr2
->second
->GetCasterGUID() != GetGUID() && IsSingleTargetSpell(spellInfo
)))
1410 if(!itr2
->second
->IsPassive())
1412 // skip all auras from spell that apply at cast SPELL_AURA_MOD_SHAPESHIFT or pet area auras.
1414 for (i
= 0; i
< 3; i
++)
1415 if (spellInfo
->EffectApplyAuraName
[i
] == SPELL_AURA_MOD_STEALTH
||
1416 spellInfo
->Effect
[i
] == SPELL_EFFECT_APPLY_AREA_AURA_OWNER
||
1417 spellInfo
->Effect
[i
] == SPELL_EFFECT_APPLY_AREA_AURA_PET
)
1422 CharacterDatabase
.PExecute("INSERT INTO pet_aura (guid,caster_guid,spell,effect_index,stackcount,amount,maxduration,remaintime,remaincharges) "
1423 "VALUES ('%u', '" I64FMTD
"', '%u', '%u', '%u', '%d', '%d', '%d', '%d')",
1424 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
->m_procCharges
));
1428 if(itr
== auras
.end())
1432 if (lastEffectPair
== itr
->first
)
1436 lastEffectPair
= itr
->first
;
1442 bool Pet::addSpell(uint16 spell_id
, uint16 active
, PetSpellState state
, uint16 slot_id
, PetSpellType type
)
1444 SpellEntry
const *spellInfo
= sSpellStore
.LookupEntry(spell_id
);
1447 // do pet spell book cleanup
1448 if(state
== PETSPELL_UNCHANGED
) // spell load case
1450 sLog
.outError("Pet::addSpell: Non-existed in SpellStore spell #%u request, deleting for all pets in `pet_spell`.",spell_id
);
1451 CharacterDatabase
.PExecute("DELETE FROM pet_spell WHERE spell = '%u'",spell_id
);
1454 sLog
.outError("Pet::addSpell: Non-existed in SpellStore spell #%u request.",spell_id
);
1459 PetSpellMap::iterator itr
= m_spells
.find(spell_id
);
1460 if (itr
!= m_spells
.end())
1462 if (itr
->second
->state
== PETSPELL_REMOVED
)
1465 m_spells
.erase(itr
);
1466 state
= PETSPELL_CHANGED
;
1468 else if (state
== PETSPELL_UNCHANGED
&& itr
->second
->state
!= PETSPELL_UNCHANGED
)
1470 // can be in case spell loading but learned at some previous spell loading
1471 itr
->second
->state
= PETSPELL_UNCHANGED
;
1478 uint32 oldspell_id
= 0;
1480 PetSpell
*newspell
= new PetSpell
;
1481 newspell
->state
= state
;
1482 newspell
->type
= type
;
1484 if(active
== ACT_DECIDE
) //active was not used before, so we save it's autocast/passive state here
1486 if(IsPassiveSpell(spell_id
))
1487 newspell
->active
= ACT_PASSIVE
;
1489 newspell
->active
= ACT_DISABLED
;
1492 newspell
->active
= active
;
1494 uint32 chainstart
= spellmgr
.GetFirstSpellInChain(spell_id
);
1496 for (PetSpellMap::iterator itr
= m_spells
.begin(); itr
!= m_spells
.end(); itr
++)
1498 if(itr
->second
->state
== PETSPELL_REMOVED
) continue;
1500 if(spellmgr
.GetFirstSpellInChain(itr
->first
) == chainstart
)
1502 slot_id
= itr
->second
->slotId
;
1503 newspell
->active
= itr
->second
->active
;
1505 if(newspell
->active
== ACT_ENABLED
)
1506 ToggleAutocast(itr
->first
, false);
1508 oldspell_id
= itr
->first
;
1509 removeSpell(itr
->first
);
1513 uint16 tmpslot
=slot_id
;
1515 if (tmpslot
== 0xffff)
1518 PetSpellMap::iterator itr
;
1519 for (itr
= m_spells
.begin(); itr
!= m_spells
.end(); ++itr
)
1521 if(itr
->second
->state
== PETSPELL_REMOVED
) continue;
1522 if (itr
->second
->slotId
> maxid
) maxid
= itr
->second
->slotId
;
1524 tmpslot
= maxid
+ 1;
1527 newspell
->slotId
= tmpslot
;
1528 m_spells
[spell_id
] = newspell
;
1530 if (IsPassiveSpell(spell_id
))
1531 CastSpell(this, spell_id
, true);
1532 else if(state
== PETSPELL_NEW
)
1533 m_charmInfo
->AddSpellToAB(oldspell_id
, spell_id
);
1535 if(newspell
->active
== ACT_ENABLED
)
1536 ToggleAutocast(spell_id
, true);
1541 bool Pet::learnSpell(uint16 spell_id
)
1543 // prevent duplicated entires in spell book
1544 if (!addSpell(spell_id
))
1547 Unit
* owner
= GetOwner();
1548 if(owner
->GetTypeId()==TYPEID_PLAYER
)
1549 ((Player
*)owner
)->PetSpellInitialize();
1553 void Pet::removeSpell(uint16 spell_id
)
1555 PetSpellMap::iterator itr
= m_spells
.find(spell_id
);
1556 if (itr
== m_spells
.end())
1559 if(itr
->second
->state
== PETSPELL_REMOVED
)
1562 if(itr
->second
->state
== PETSPELL_NEW
)
1565 m_spells
.erase(itr
);
1568 itr
->second
->state
= PETSPELL_REMOVED
;
1570 RemoveAurasDueToSpell(spell_id
);
1573 bool Pet::_removeSpell(uint16 spell_id
)
1575 PetSpellMap::iterator itr
= m_spells
.find(spell_id
);
1576 if (itr
!= m_spells
.end())
1579 m_spells
.erase(itr
);
1585 void Pet::InitPetCreateSpells()
1587 m_charmInfo
->InitPetActionBar();
1590 int32 usedtrainpoints
= 0, petspellid
;
1591 PetCreateSpellEntry
const* CreateSpells
= objmgr
.GetPetCreateSpellEntry(GetEntry());
1594 for(uint8 i
= 0; i
< 4; i
++)
1596 if(!CreateSpells
->spellid
[i
])
1599 SpellEntry
const *learn_spellproto
= sSpellStore
.LookupEntry(CreateSpells
->spellid
[i
]);
1600 if(!learn_spellproto
)
1603 if(learn_spellproto
->Effect
[0] == SPELL_EFFECT_LEARN_SPELL
|| learn_spellproto
->Effect
[0] == SPELL_EFFECT_LEARN_PET_SPELL
)
1605 petspellid
= learn_spellproto
->EffectTriggerSpell
[0];
1606 Unit
* owner
= GetOwner();
1607 if(owner
->GetTypeId() == TYPEID_PLAYER
&& !((Player
*)owner
)->HasSpell(learn_spellproto
->Id
))
1609 if(IsPassiveSpell(petspellid
)) //learn passive skills when tamed, not sure if thats right
1610 ((Player
*)owner
)->learnSpell(learn_spellproto
->Id
);
1612 AddTeachSpell(learn_spellproto
->EffectTriggerSpell
[0], learn_spellproto
->Id
);
1616 petspellid
= learn_spellproto
->Id
;
1618 addSpell(petspellid
);
1620 SkillLineAbilityMap::const_iterator lower
= spellmgr
.GetBeginSkillLineAbilityMap(learn_spellproto
->EffectTriggerSpell
[0]);
1621 SkillLineAbilityMap::const_iterator upper
= spellmgr
.GetEndSkillLineAbilityMap(learn_spellproto
->EffectTriggerSpell
[0]);
1623 for(SkillLineAbilityMap::const_iterator _spell_idx
= lower
; _spell_idx
!= upper
; ++_spell_idx
)
1625 usedtrainpoints
+= _spell_idx
->second
->reqtrainpoints
;
1633 CastPetAuras(false);
1635 SetTP(-usedtrainpoints
);
1638 void Pet::CheckLearning(uint32 spellid
)
1640 //charmed case -> prevent crash
1641 if(GetTypeId() == TYPEID_PLAYER
|| getPetType() != HUNTER_PET
)
1644 Unit
* owner
= GetOwner();
1646 if(m_teachspells
.empty() || !owner
|| owner
->GetTypeId() != TYPEID_PLAYER
)
1649 TeachSpellMap::iterator itr
= m_teachspells
.find(spellid
);
1650 if(itr
== m_teachspells
.end())
1653 if(urand(0, 100) < 10)
1655 ((Player
*)owner
)->learnSpell(itr
->second
);
1656 m_teachspells
.erase(itr
);
1660 uint32
Pet::resetTalentsCost() const
1662 uint32 days
= (sWorld
.GetGameTime() - m_resetTalentsTime
)/DAY
;
1664 // The first time reset costs 10 silver; after 1 day cost is reset to 10 silver
1665 if(m_resetTalentsCost
< 10*SILVER
|| days
> 0)
1668 else if(m_resetTalentsCost
< 50*SILVER
)
1671 else if(m_resetTalentsCost
< 1*GOLD
)
1673 // then increasing at a rate of 1 gold; cap 10 gold
1675 return (m_resetTalentsCost
+ 1*GOLD
> 10*GOLD
? 10*GOLD
: m_resetTalentsCost
+ 1*GOLD
);
1678 void Pet::ToggleAutocast(uint32 spellid
, bool apply
)
1680 if(IsPassiveSpell(spellid
))
1683 PetSpellMap::const_iterator itr
= m_spells
.find((uint16
)spellid
);
1689 for (i
= 0; i
< m_autospells
.size() && m_autospells
[i
] != spellid
; i
++);
1690 if (i
== m_autospells
.size())
1692 m_autospells
.push_back(spellid
);
1693 itr
->second
->active
= ACT_ENABLED
;
1694 itr
->second
->state
= PETSPELL_CHANGED
;
1699 AutoSpellList::iterator itr2
= m_autospells
.begin();
1700 for (i
= 0; i
< m_autospells
.size() && m_autospells
[i
] != spellid
; i
++, itr2
++);
1701 if (i
< m_autospells
.size())
1703 m_autospells
.erase(itr2
);
1704 itr
->second
->active
= ACT_DISABLED
;
1705 itr
->second
->state
= PETSPELL_CHANGED
;
1710 bool Pet::Create(uint32 guidlow
, Map
*map
, uint32 Entry
, uint32 pet_number
)
1712 SetMapId(map
->GetId());
1713 SetInstanceId(map
->GetInstanceId());
1715 Object::_Create(guidlow
, pet_number
, HIGHGUID_PET
);
1717 m_DBTableGuid
= guidlow
;
1718 m_originalEntry
= Entry
;
1720 if(!InitEntry(Entry
))
1723 SetByteValue(UNIT_FIELD_BYTES_2
, 0, SHEATH_STATE_MELEE
);
1724 SetByteValue(UNIT_FIELD_BYTES_2
, 1, UNIT_BYTE2_FLAG_UNK3
| UNIT_BYTE2_FLAG_AURAS
| UNIT_BYTE2_FLAG_UNK5
);
1726 if(getPetType() == MINI_PET
) // always non-attackable
1727 SetFlag(UNIT_FIELD_FLAGS
, UNIT_FLAG_NON_ATTACKABLE
);
1732 bool Pet::HasSpell(uint32 spell
) const
1734 return (m_spells
.find(spell
) != m_spells
.end());
1737 // Get all passive spells in our skill line
1738 void Pet::LearnPetPassives()
1740 CreatureInfo
const* cInfo
= GetCreatureInfo();
1744 CreatureFamilyEntry
const* cFamily
= sCreatureFamilyStore
.LookupEntry(cInfo
->family
);
1748 PetFamilySpellsStore::const_iterator petStore
= sPetFamilySpellsStore
.find(cFamily
->ID
);
1749 if(petStore
!= sPetFamilySpellsStore
.end())
1751 for(PetFamilySpellsSet::const_iterator petSet
= petStore
->second
.begin(); petSet
!= petStore
->second
.end(); ++petSet
)
1752 addSpell(*petSet
, ACT_DECIDE
, PETSPELL_NEW
, 0xffff, PETSPELL_FAMILY
);
1756 void Pet::CastPetAuras(bool current
)
1758 Unit
* owner
= GetOwner();
1762 if(getPetType() != HUNTER_PET
&& (getPetType() != SUMMON_PET
|| owner
->getClass() != CLASS_WARLOCK
))
1765 for(PetAuraSet::iterator itr
= owner
->m_petAuras
.begin(); itr
!= owner
->m_petAuras
.end(); )
1767 PetAura
const* pa
= *itr
;
1770 if(!current
&& pa
->IsRemovedOnChangePet())
1771 owner
->RemovePetAura(pa
);
1777 void Pet::CastPetAura(PetAura
const* aura
)
1779 uint16 auraId
= aura
->GetAura(GetEntry());
1783 if(auraId
== 35696) // Demonic Knowledge
1785 int32 basePoints
= int32(aura
->GetDamage() * (GetStat(STAT_STAMINA
) + GetStat(STAT_INTELLECT
)) / 100);
1786 CastCustomSpell(this,auraId
,&basePoints
, NULL
, NULL
, true );
1789 CastSpell(this, auraId
, true);