2008-11-17 22:07:21 -06:00
/*
2009-02-04 12:42:26 +01:00
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
2008-11-17 22:07:21 -06:00
*
2010-01-16 20:19:18 +03:00
* Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
2008-11-17 22:07:21 -06:00
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
#include "Common.h"
#include "Database/DatabaseEnv.h"
#include "Log.h"
#include "WorldPacket.h"
#include "ObjectMgr.h"
#include "SpellMgr.h"
#include "Pet.h"
#include "Formulas.h"
#include "SpellAuras.h"
2010-01-10 01:23:15 +01:00
#include "SpellAuraEffects.h"
2008-11-17 22:07:21 -06:00
#include "CreatureAI.h"
#include "Unit.h"
#include "Util.h"
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
char const * petTypeSuffix [ MAX_PET_TYPE ] =
{
"'s Minion" , // SUMMON_PET
"'s Pet" , // HUNTER_PET
"'s Guardian" , // GUARDIAN_PET
"'s Companion" // MINI_PET
};
2009-10-17 15:51:44 -07:00
2010-02-14 19:13:14 -07:00
#define PET_XP_FACTOR 0.05f
2009-10-17 15:51:44 -07:00
2009-03-25 21:32:34 -06:00
Pet :: Pet ( Player * owner , PetType type ) : Guardian ( NULL , owner ),
m_petType ( type ), m_removed ( false ), m_happinessTimer ( 7500 ), m_duration ( 0 ),
2009-02-06 19:42:03 +01:00
m_resetTalentsCost ( 0 ), m_resetTalentsTime ( 0 ), m_usedTalentCount ( 0 ), m_auraRaidUpdateMask ( 0 ), m_loading ( false ),
2009-03-27 22:47:28 -06:00
m_declinedname ( NULL ), m_owner ( owner )
2008-11-17 22:07:21 -06:00
{
2009-08-23 22:09:43 -05:00
m_unitTypeMask |= UNIT_MASK_PET ;
2009-06-01 23:45:00 -05:00
if ( type == HUNTER_PET )
2009-08-23 22:09:43 -05:00
m_unitTypeMask |= UNIT_MASK_HUNTER_PET ;
2009-10-17 15:51:44 -07:00
2009-08-23 22:09:43 -05:00
if ( ! ( m_unitTypeMask & UNIT_MASK_CONTROLABLE_GUARDIAN ))
2009-07-22 19:11:16 +02:00
{
2009-08-23 22:09:43 -05:00
m_unitTypeMask |= UNIT_MASK_CONTROLABLE_GUARDIAN ;
2009-07-22 19:11:16 +02:00
InitCharmInfo ();
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
m_name = "Pet" ;
2010-01-10 01:23:15 +01:00
m_regenTimer = PET_FOCUS_REGEN_INTERVAL ;
2009-10-17 15:51:44 -07:00
2009-08-11 16:14:45 -05:00
m_isWorldObject = true ;
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
Pet ::~ Pet ()
{
delete m_declinedname ;
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
void Pet :: AddToWorld ()
{
///- Register the pet for guid lookup
2009-03-23 16:54:43 -06:00
if ( ! IsInWorld ())
{
///- Register the pet for guid lookup
ObjectAccessor :: Instance (). AddObject ( this );
Unit :: AddToWorld ();
2009-03-31 09:18:39 -06:00
AIM_Initialize ();
2009-03-23 16:54:43 -06:00
}
2009-10-17 15:51:44 -07:00
2009-09-10 09:59:16 -07:00
// Prevent stuck pets when zoning. Pets default to "follow" when added to world
// so we'll reset flags and let the AI handle things
if ( this -> GetCharmInfo () && this -> GetCharmInfo () -> HasCommandState ( COMMAND_FOLLOW ))
{
this -> GetCharmInfo () -> SetIsCommandAttack ( false );
this -> GetCharmInfo () -> SetIsAtStay ( false );
this -> GetCharmInfo () -> SetIsFollowing ( false );
this -> GetCharmInfo () -> SetIsReturning ( false );
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
void Pet :: RemoveFromWorld ()
{
///- Remove the pet from the accessor
2009-03-23 16:54:43 -06:00
if ( IsInWorld ())
{
///- Don't call the function for Creature, normal mobs + totems go in a different storage
Unit :: RemoveFromWorld ();
2009-03-28 16:04:37 -06:00
ObjectAccessor :: Instance (). RemoveObject ( this );
2009-03-23 16:54:43 -06:00
}
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2008-12-26 17:08:59 -06:00
bool Pet :: LoadPetFromDB ( Player * owner , uint32 petentry , uint32 petnumber , bool current )
2008-11-17 22:07:21 -06:00
{
2008-12-24 09:58:26 -06:00
m_loading = true ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
uint32 ownerid = owner -> GetGUIDLow ();
2009-10-17 15:51:44 -07:00
2010-01-23 14:45:58 +01:00
QueryResult_AutoPtr result ;
2009-10-17 15:51:44 -07:00
2009-04-11 14:47:38 -05:00
if ( petnumber )
2009-05-28 18:22:54 -05:00
// known petnumber entry 0 1 2(?) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
2009-06-23 18:50:33 +02:00
result = CharacterDatabase . PQuery ( "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType "
2009-04-10 18:18:44 -05:00
"FROM character_pet WHERE owner = '%u' AND id = '%u'" ,
ownerid , petnumber );
2009-04-11 14:47:38 -05:00
else if ( current )
2009-05-28 18:22:54 -05:00
// current pet (slot 0) 0 1 2(?) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
2009-06-23 18:50:33 +02:00
result = CharacterDatabase . PQuery ( "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType "
2009-04-10 18:18:44 -05:00
"FROM character_pet WHERE owner = '%u' AND slot = '%u'" ,
ownerid , PET_SAVE_AS_CURRENT );
2009-04-11 14:47:38 -05:00
else if ( petentry )
2008-11-17 22:07:21 -06:00
// known petentry entry (unique for summoned pet, but non unique for hunter pet (only from current or not stabled pets)
2009-05-28 18:22:54 -05:00
// 0 1 2(?) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
2009-06-23 18:50:33 +02:00
result = CharacterDatabase . PQuery ( "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType "
2009-04-10 18:18:44 -05:00
"FROM character_pet WHERE owner = '%u' AND entry = '%u' AND (slot = '%u' OR slot > '%u') " ,
ownerid , petentry , PET_SAVE_AS_CURRENT , PET_SAVE_LAST_STABLE_SLOT );
2008-11-17 22:07:21 -06:00
else
// any current or other non-stabled pet (for hunter "call pet")
2009-05-28 18:22:54 -05:00
// 0 1 2(?) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
2009-06-23 18:50:33 +02:00
result = CharacterDatabase . PQuery ( "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType "
2009-04-10 18:18:44 -05:00
"FROM character_pet WHERE owner = '%u' AND (slot = '%u' OR slot > '%u') " ,
ownerid , PET_SAVE_AS_CURRENT , PET_SAVE_LAST_STABLE_SLOT );
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
if ( ! result )
return false ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
Field * fields = result -> Fetch ();
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
// update for case of current pet "slot = 0"
petentry = fields [ 1 ]. GetUInt32 ();
2009-04-11 14:47:38 -05:00
if ( ! petentry )
2008-11-17 22:07:21 -06:00
return false ;
2009-10-17 15:51:44 -07:00
2009-05-28 18:22:54 -05:00
uint32 summon_spell_id = fields [ 17 ]. GetUInt32 ();
2008-11-17 22:07:21 -06:00
SpellEntry const * spellInfo = sSpellStore . LookupEntry ( summon_spell_id );
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
bool is_temporary_summoned = spellInfo && GetSpellDuration ( spellInfo ) > 0 ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
// check temporary summoned pets like mage water elemental
2009-04-11 14:47:38 -05:00
if ( current && is_temporary_summoned )
2008-11-17 22:07:21 -06:00
return false ;
2009-10-17 15:51:44 -07:00
2009-06-06 20:31:23 -05:00
PetType pet_type = PetType ( fields [ 18 ]. GetUInt8 ());
if ( pet_type == HUNTER_PET )
{
CreatureInfo const * creatureInfo = objmgr . GetCreatureTemplate ( petentry );
if ( ! creatureInfo || ! creatureInfo -> isTameable ( owner -> CanTameExoticPets ()))
return false ;
}
2009-10-17 15:51:44 -07:00
2009-06-06 20:31:23 -05:00
uint32 pet_number = fields [ 0 ]. GetUInt32 ();
2009-10-17 15:51:44 -07:00
2009-04-11 14:47:38 -05:00
if ( current && owner -> IsPetNeedBeTemporaryUnsummoned ())
{
owner -> SetTemporaryUnsummonedPetNumber ( pet_number );
return false ;
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
Map * map = owner -> GetMap ();
2008-12-12 11:21:28 -06:00
uint32 guid = objmgr . GenerateLowGuid ( HIGHGUID_PET );
2009-04-11 14:47:38 -05:00
if ( ! Create ( guid , map , owner -> GetPhaseMask (), petentry , pet_number ))
2008-11-17 22:07:21 -06:00
return false ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
float px , py , pz ;
2009-07-22 17:10:30 +02:00
owner -> GetClosePoint ( px , py , pz , GetObjectSize (), PET_FOLLOW_DIST , GetFollowAngle ());
2008-11-17 22:07:21 -06:00
Relocate ( px , py , pz , owner -> GetOrientation ());
2009-10-17 15:51:44 -07:00
2009-04-11 14:47:38 -05:00
if ( ! IsPositionValid ())
2008-11-17 22:07:21 -06:00
{
2009-03-21 14:28:02 -06:00
sLog . outError ( "Pet (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)" ,
2008-11-17 22:07:21 -06:00
GetGUIDLow (), GetEntry (), GetPositionX (), GetPositionY ());
return false ;
}
2009-10-17 15:51:44 -07:00
2009-06-06 20:31:23 -05:00
setPetType ( pet_type );
2009-06-03 19:29:55 -05:00
setFaction ( owner -> getFaction ());
2008-11-17 22:07:21 -06:00
SetUInt32Value ( UNIT_CREATED_BY_SPELL , summon_spell_id );
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
CreatureInfo const * cinfo = GetCreatureInfo ();
2009-04-11 14:47:38 -05:00
if ( cinfo -> type == CREATURE_TYPE_CRITTER )
2008-11-17 22:07:21 -06:00
{
2010-03-07 18:30:53 +01:00
map -> Add ( this -> ToCreature ());
2008-11-17 22:07:21 -06:00
return true ;
}
2009-10-17 15:51:44 -07:00
2009-05-08 18:43:06 -05:00
m_charmInfo -> SetPetNumber ( pet_number , IsPermanentPetFor ( owner ));
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
SetDisplayId ( fields [ 3 ]. GetUInt32 ());
SetNativeDisplayId ( fields [ 3 ]. GetUInt32 ());
2008-12-24 09:58:26 -06:00
uint32 petlevel = fields [ 4 ]. GetUInt32 ();
2009-06-03 19:29:55 -05:00
SetUInt32Value ( UNIT_NPC_FLAGS , UNIT_NPC_FLAG_NONE );
2009-05-28 18:22:54 -05:00
SetName ( fields [ 8 ]. GetString ());
2009-10-17 15:51:44 -07:00
2009-04-11 14:47:38 -05:00
switch ( getPetType ())
2008-11-17 22:07:21 -06:00
{
case SUMMON_PET :
petlevel = owner -> getLevel ();
2009-10-17 15:51:44 -07:00
2009-06-09 16:29:23 -05:00
SetUInt32Value ( UNIT_FIELD_BYTES_0 , 0x800 ); //class=mage
2008-11-17 22:07:21 -06:00
SetUInt32Value ( UNIT_FIELD_FLAGS , UNIT_FLAG_PVP_ATTACKABLE );
// this enables popup window (pet dismiss, cancel)
break ;
case HUNTER_PET :
2009-06-09 16:29:23 -05:00
SetUInt32Value ( UNIT_FIELD_BYTES_0 , 0x02020100 ); //class=warrior,gender=none,power=focus
2009-05-28 18:43:57 -05:00
SetSheath ( SHEATH_STATE_MELEE );
2010-02-14 19:13:14 -07:00
SetByteFlag ( UNIT_FIELD_BYTES_2 , 2 , fields [ 9 ]. GetBool () ? UNIT_CAN_BE_ABANDONED : UNIT_CAN_BE_RENAMED | UNIT_CAN_BE_ABANDONED );
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
SetUInt32Value ( UNIT_FIELD_FLAGS , UNIT_FLAG_PVP_ATTACKABLE );
// this enables popup window (pet abandon, cancel)
2008-12-24 09:58:26 -06:00
SetMaxPower ( POWER_HAPPINESS , GetCreatePowers ( POWER_HAPPINESS ));
2009-05-28 18:22:54 -05:00
SetPower ( POWER_HAPPINESS , fields [ 12 ]. GetUInt32 ());
2008-11-17 22:07:21 -06:00
setPowerType ( POWER_FOCUS );
break ;
default :
2009-08-24 12:05:50 -05:00
if ( ! IsPetGhoul ())
sLog . outError ( "Pet have incorrect type (%u) for pet loading." , getPetType ());
break ;
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2009-12-24 10:20:15 +01:00
SetUInt32Value ( UNIT_FIELD_PET_NAME_TIMESTAMP , time ( NULL ));
2008-11-17 22:07:21 -06:00
SetUInt32Value ( UNIT_FIELD_PETEXPERIENCE , fields [ 5 ]. GetUInt32 ());
2008-12-24 09:58:26 -06:00
SetCreatorGUID ( owner -> GetGUID ());
2009-10-17 15:51:44 -07:00
2009-03-05 18:48:56 -06:00
SetReactState ( ReactStates ( fields [ 6 ]. GetUInt8 () ));
2009-10-17 15:51:44 -07:00
2009-04-02 22:03:25 -06:00
SetCanModifyStats ( true );
2009-04-02 19:05:17 -06:00
InitStatsForLevel ( petlevel );
2009-10-17 15:51:44 -07:00
2009-04-02 22:03:25 -06:00
if ( getPetType () == SUMMON_PET && ! current ) //all (?) summon pets come with full health when called, but not when they are current
2008-11-17 22:07:21 -06:00
{
2009-04-02 22:03:25 -06:00
SetHealth ( GetMaxHealth ());
SetPower ( POWER_MANA , GetMaxPower ( POWER_MANA ));
}
else
{
2009-05-28 18:22:54 -05:00
uint32 savedhealth = fields [ 10 ]. GetUInt32 ();
uint32 savedmana = fields [ 11 ]. GetUInt32 ();
2010-01-16 19:11:32 +03:00
if ( ! savedhealth && getPetType () == HUNTER_PET )
setDeathState ( JUST_DIED );
else
{
SetHealth ( savedhealth > GetMaxHealth () ? GetMaxHealth () : savedhealth );
SetPower ( POWER_MANA , savedmana > GetMaxPower ( POWER_MANA ) ? GetMaxPower ( POWER_MANA ) : savedmana );
}
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2009-05-28 18:22:54 -05:00
// set current pet as current
// 0=current
// 1..MAX_PET_STABLES in stable slot
// PET_SAVE_NOT_IN_SLOT(100) = not stable slot (summoning))
if ( fields [ 7 ]. GetUInt32 () != 0 )
{
CharacterDatabase . BeginTransaction ();
CharacterDatabase . PExecute ( "UPDATE character_pet SET slot = '%u' WHERE owner = '%u' AND slot = '%u' AND id <> '%u'" ,
PET_SAVE_NOT_IN_SLOT , ownerid , PET_SAVE_AS_CURRENT , m_charmInfo -> GetPetNumber ());
CharacterDatabase . PExecute ( "UPDATE character_pet SET slot = '%u' WHERE owner = '%u' AND id = '%u'" ,
PET_SAVE_AS_CURRENT , ownerid , m_charmInfo -> GetPetNumber ());
CharacterDatabase . CommitTransaction ();
}
2009-10-17 15:51:44 -07:00
2009-06-18 14:17:11 +02:00
// Send fake summon spell cast - this is needed for correct cooldown application for spells
// Example: 46584 - without this cooldown (which should be set always when pet is loaded) isn't set clientside
// TODO: pets should be summoned from real cast instead of just faking it?
if ( GetUInt32Value ( UNIT_CREATED_BY_SPELL ))
{
WorldPacket data ( SMSG_SPELL_GO , ( 8 + 8 + 4 + 4 + 2 ));
data . append ( owner -> GetPackGUID ());
data . append ( owner -> GetPackGUID ());
data << uint8 ( 0 );
data << uint32 ( GetUInt32Value ( UNIT_CREATED_BY_SPELL ));
data << uint32 ( 256 ); // CAST_FLAG_UNKNOWN3
data << uint32 ( 0 );
SendMessageToSet ( & data , true );
}
2009-10-17 15:51:44 -07:00
2009-05-02 19:24:22 -05:00
owner -> SetMinion ( this , true );
2010-03-07 18:30:53 +01:00
map -> Add ( this -> ToCreature ());
2009-10-17 15:51:44 -07:00
2009-05-28 18:22:54 -05:00
m_resetTalentsCost = fields [ 15 ]. GetUInt32 ();
m_resetTalentsTime = fields [ 16 ]. GetUInt64 ();
InitTalentForLevel (); // set original talents points before spell loading
2009-10-17 15:51:44 -07:00
2009-12-24 10:20:15 +01:00
uint32 timediff = ( time ( NULL ) - fields [ 14 ]. GetUInt32 ());
2009-05-08 17:15:19 -05:00
_LoadAuras ( timediff );
2009-10-17 15:51:44 -07:00
2009-06-23 13:05:15 +02:00
// load action bar, if data broken will fill later by default spells.
if ( ! is_temporary_summoned )
2009-04-26 02:48:20 +02:00
{
2009-06-23 13:05:15 +02:00
m_charmInfo -> LoadPetActionBar ( fields [ 13 ]. GetCppString ());
2009-10-17 15:51:44 -07:00
2009-06-23 13:05:15 +02:00
_LoadSpells ();
_LoadSpellCooldowns ();
LearnPetPassives ();
InitLevelupSpellsForLevel ();
CastPetAuras ( current );
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2009-05-27 17:02:39 -05:00
CleanupActionBar (); // remove unknown spells from action bar after load
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
sLog . outDebug ( "New Pet has guid %u" , GetGUIDLow ());
2009-10-17 15:51:44 -07:00
2009-03-27 22:47:28 -06:00
owner -> PetSpellInitialize ();
2009-10-17 15:51:44 -07:00
2009-03-27 22:47:28 -06:00
if ( owner -> GetGroup ())
owner -> SetGroupUpdateFlag ( GROUP_UPDATE_PET );
2009-10-17 15:51:44 -07:00
2009-06-11 00:45:59 -05:00
owner -> SendTalentsInfoData ( true );
2009-10-17 15:51:44 -07:00
2009-03-27 22:47:28 -06:00
if ( getPetType () == HUNTER_PET )
2008-11-17 22:07:21 -06:00
{
2008-12-12 11:21:28 -06:00
result = CharacterDatabase . PQuery ( "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_pet_declinedname WHERE owner = '%u' AND id = '%u'" , owner -> GetGUIDLow (), GetCharmInfo () -> GetPetNumber ());
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
if ( result )
{
if ( m_declinedname )
delete m_declinedname ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
m_declinedname = new DeclinedName ;
2009-04-27 18:42:35 -05:00
Field * fields2 = result -> Fetch ();
2009-10-17 16:20:24 -07:00
for ( uint8 i = 0 ; i < MAX_DECLINED_NAME_CASES ; ++ i )
2008-11-17 22:07:21 -06:00
{
2009-04-27 18:42:35 -05:00
m_declinedname -> name [ i ] = fields2 [ i ]. GetCppString ();
2008-11-17 22:07:21 -06:00
}
}
}
2009-10-17 15:51:44 -07:00
2009-11-17 20:26:35 -08:00
//set last used pet number (for use in BG's)
2009-10-06 22:41:37 +02:00
if ( owner -> GetTypeId () == TYPEID_PLAYER && isControlled () && ! isTemporarySummoned () && ( getPetType () == SUMMON_PET || getPetType () == HUNTER_PET ))
2010-03-07 15:20:19 +01:00
owner -> ToPlayer () -> SetLastPetNumber ( pet_number );
2009-10-17 15:51:44 -07:00
2008-12-24 09:58:26 -06:00
m_loading = false ;
2009-10-17 15:51:44 -07:00
2009-05-15 13:44:49 -05:00
SynchronizeLevelWithOwner ();
2008-11-17 22:07:21 -06:00
return true ;
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
void Pet :: SavePetToDB ( PetSaveMode mode )
{
2009-04-11 14:47:38 -05:00
if ( ! GetEntry ())
2008-11-17 22:07:21 -06:00
return ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
// save only fully controlled creature
2009-04-11 14:47:38 -05:00
if ( ! isControlled ())
2008-11-17 22:07:21 -06:00
return ;
2009-10-17 15:51:44 -07:00
2009-04-11 14:47:38 -05:00
// not save not player pets
if ( ! IS_PLAYER_GUID ( GetOwnerGUID ()))
return ;
2009-10-17 15:51:44 -07:00
2009-04-11 14:47:38 -05:00
Player * pOwner = ( Player * ) GetOwner ();
if ( ! pOwner )
return ;
2009-10-17 15:51:44 -07:00
2009-04-11 14:47:38 -05:00
// not save pet as current if another pet temporary unsummoned
if ( mode == PET_SAVE_AS_CURRENT && pOwner -> GetTemporaryUnsummonedPetNumber () &&
pOwner -> GetTemporaryUnsummonedPetNumber () != m_charmInfo -> GetPetNumber ())
{
// pet will lost anyway at restore temporary unsummoned
if ( getPetType () == HUNTER_PET )
return ;
2009-10-17 15:51:44 -07:00
2009-04-11 14:47:38 -05:00
// for warlock case
mode = PET_SAVE_NOT_IN_SLOT ;
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
uint32 curhealth = GetHealth ();
uint32 curmana = GetPower ( POWER_MANA );
2009-10-17 15:51:44 -07:00
2009-04-10 18:18:44 -05:00
// stable and not in slot saves
if ( mode > PET_SAVE_AS_CURRENT )
2008-11-17 22:07:21 -06:00
{
2009-04-10 18:18:44 -05:00
RemoveAllAuras ();
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
_SaveSpells ();
_SaveSpellCooldowns ();
_SaveAuras ();
2009-10-17 15:51:44 -07:00
2009-04-10 18:18:44 -05:00
// current/stable/not_in_slot
if ( mode >= PET_SAVE_AS_CURRENT )
2008-11-17 22:07:21 -06:00
{
2009-04-10 18:18:44 -05:00
uint32 owner = GUID_LOPART ( GetOwnerGUID ());
std :: string name = m_name ;
CharacterDatabase . escape_string ( name );
CharacterDatabase . BeginTransaction ();
// remove current data
CharacterDatabase . PExecute ( "DELETE FROM character_pet WHERE owner = '%u' AND id = '%u'" , owner , m_charmInfo -> GetPetNumber () );
2009-10-17 15:51:44 -07:00
2009-04-10 18:18:44 -05:00
// prevent duplicate using slot (except PET_SAVE_NOT_IN_SLOT)
if ( mode <= PET_SAVE_LAST_STABLE_SLOT )
CharacterDatabase . PExecute ( "UPDATE character_pet SET slot = '%u' WHERE owner = '%u' AND slot = '%u'" ,
PET_SAVE_NOT_IN_SLOT , owner , uint32 ( mode ) );
2009-10-17 15:51:44 -07:00
2009-04-10 18:18:44 -05:00
// prevent existence another hunter pet in PET_SAVE_AS_CURRENT and PET_SAVE_NOT_IN_SLOT
if ( getPetType () == HUNTER_PET && ( mode == PET_SAVE_AS_CURRENT || mode > PET_SAVE_LAST_STABLE_SLOT ))
CharacterDatabase . PExecute ( "DELETE FROM character_pet WHERE owner = '%u' AND (slot = '%u' OR slot > '%u')" ,
owner , PET_SAVE_AS_CURRENT , PET_SAVE_LAST_STABLE_SLOT );
// save pet
std :: ostringstream ss ;
2009-06-23 13:05:15 +02:00
ss << "INSERT INTO character_pet ( id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType) "
2009-04-10 18:18:44 -05:00
<< "VALUES ("
<< m_charmInfo -> GetPetNumber () << ", "
<< GetEntry () << ", "
<< owner << ", "
<< GetNativeDisplayId () << ", "
2009-11-01 20:50:31 -08:00
<< uint32 ( getLevel ()) << ", "
2009-04-10 18:18:44 -05:00
<< GetUInt32Value ( UNIT_FIELD_PETEXPERIENCE ) << ", "
<< uint32 ( GetReactState ()) << ", "
<< uint32 ( mode ) << ", '"
<< name . c_str () << "', "
2010-02-14 19:13:14 -07:00
<< uint32 ( HasByteFlag ( UNIT_FIELD_BYTES_2 , 2 , UNIT_CAN_BE_RENAMED ) ? 0 : 1 ) << ", "
2010-01-16 19:11:32 +03:00
<< curhealth << ", "
2009-04-10 18:18:44 -05:00
<< curmana << ", "
<< GetPower ( POWER_HAPPINESS ) << ", '" ;
2009-10-17 15:51:44 -07:00
2009-12-17 18:21:06 +01:00
for ( uint32 i = ACTION_BAR_INDEX_START ; i < ACTION_BAR_INDEX_END ; ++ i )
2009-05-27 17:02:39 -05:00
{
2009-07-01 18:07:20 -05:00
ss << uint32 ( m_charmInfo -> GetActionBarEntry ( i ) -> GetType ()) << " "
<< uint32 ( m_charmInfo -> GetActionBarEntry ( i ) -> GetAction ()) << " " ;
2009-05-27 17:02:39 -05:00
};
2009-10-17 15:51:44 -07:00
2009-04-10 18:18:44 -05:00
ss << "', "
2009-12-24 10:20:15 +01:00
<< time ( NULL ) << ", "
2009-04-10 18:18:44 -05:00
<< uint32 ( m_resetTalentsCost ) << ", "
<< uint64 ( m_resetTalentsTime ) << ", "
<< GetUInt32Value ( UNIT_CREATED_BY_SPELL ) << ", "
2009-06-23 13:05:15 +02:00
<< uint32 ( getPetType ()) << ")" ;
2009-10-17 15:51:44 -07:00
2009-04-10 18:18:44 -05:00
CharacterDatabase . Execute ( ss . str (). c_str () );
CharacterDatabase . CommitTransaction ();
}
// delete
else
{
RemoveAllAuras ();
DeleteFromDB ( m_charmInfo -> GetPetNumber ());
2008-11-17 22:07:21 -06:00
}
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
void Pet :: DeleteFromDB ( uint32 guidlow )
{
CharacterDatabase . PExecute ( "DELETE FROM character_pet WHERE id = '%u'" , guidlow );
CharacterDatabase . PExecute ( "DELETE FROM character_pet_declinedname WHERE id = '%u'" , guidlow );
CharacterDatabase . PExecute ( "DELETE FROM pet_aura WHERE guid = '%u'" , guidlow );
CharacterDatabase . PExecute ( "DELETE FROM pet_spell WHERE guid = '%u'" , guidlow );
CharacterDatabase . PExecute ( "DELETE FROM pet_spell_cooldown WHERE guid = '%u'" , guidlow );
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
void Pet :: setDeathState ( DeathState s ) // overwrite virtual Creature::setDeathState and Unit::setDeathState
{
Creature :: setDeathState ( s );
if ( getDeathState () == CORPSE )
{
2009-04-13 12:11:36 -05:00
if ( getPetType () == HUNTER_PET )
2008-11-17 22:07:21 -06:00
{
// pet corpse non lootable and non skinnable
SetUInt32Value ( UNIT_DYNAMIC_FLAGS , 0x00 );
RemoveFlag ( UNIT_FIELD_FLAGS , UNIT_FLAG_SKINNABLE );
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
//lose happiness when died and not in BG/Arena
MapEntry const * mapEntry = sMapStore . LookupEntry ( GetMapId ());
if ( ! mapEntry || ( mapEntry -> map_type != MAP_ARENA && mapEntry -> map_type != MAP_BATTLEGROUND ))
ModifyPower ( POWER_HAPPINESS , - HAPPINESS_LEVEL_SIZE );
2009-10-17 15:51:44 -07:00
2009-06-09 19:33:38 +02:00
//SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);
2008-11-17 22:07:21 -06:00
}
}
else if ( getDeathState () == ALIVE )
{
2009-06-09 19:33:38 +02:00
//RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);
2008-11-17 22:07:21 -06:00
CastPetAuras ( true );
}
}
2009-10-17 15:51:44 -07:00
2009-09-10 08:48:25 -07:00
void Pet :: Update ( uint32 diff )
2008-11-17 22:07:21 -06:00
{
2010-01-13 18:24:23 +01:00
if ( m_removed ) // pet already removed, just wait in remove queue, no updates
return ;
if ( m_loading )
2008-11-17 22:07:21 -06:00
return ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
switch ( m_deathState )
{
case CORPSE :
{
2009-04-13 12:11:36 -05:00
if ( getPetType () != HUNTER_PET || m_deathTimer <= diff )
2008-11-17 22:07:21 -06:00
{
Remove ( PET_SAVE_NOT_IN_SLOT ); //hunters' pets never get removed because of death, NEVER!
return ;
}
break ;
}
case ALIVE :
{
// unsummon pet that lost owner
2009-03-27 22:47:28 -06:00
Player * owner = GetOwner ();
2009-12-19 20:02:38 +01:00
if ( ! owner || ( ! IsWithinDistInMap ( owner , GetMap () -> GetVisibilityDistance ()) && ! isPossessed ()) || isControlled () && ! owner -> GetPetGUID ())
//if(!owner || (!IsWithinDistInMap(owner, GetMap()->GetVisibilityDistance()) && (owner->GetCharmGUID() && (owner->GetCharmGUID() != GetGUID()))) || (isControlled() && !owner->GetPetGUID()))
2008-11-17 22:07:21 -06:00
{
Remove ( PET_SAVE_NOT_IN_SLOT , true );
return ;
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
if ( isControlled ())
{
if ( owner -> GetPetGUID () != GetGUID () )
{
2009-03-27 22:47:28 -06:00
sLog . outError ( "Pet %u is not pet of owner %u, removed" , GetEntry (), m_owner -> GetName ());
2008-11-17 22:07:21 -06:00
Remove ( getPetType () == HUNTER_PET ? PET_SAVE_AS_DELETED : PET_SAVE_NOT_IN_SLOT );
return ;
}
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
if ( m_duration > 0 )
{
if ( m_duration > diff )
m_duration -= diff ;
else
{
Remove ( getPetType () != SUMMON_PET ? PET_SAVE_AS_DELETED : PET_SAVE_NOT_IN_SLOT );
return ;
}
}
2009-10-17 15:51:44 -07:00
2009-05-06 16:47:06 -05:00
//regenerate focus for hunter pets or energy for deathknight's ghoul
2009-08-23 22:20:34 -05:00
if ( m_regenTimer )
2008-11-17 22:07:21 -06:00
{
2009-08-23 22:20:34 -05:00
if ( m_regenTimer > diff )
m_regenTimer -= diff ;
else
2009-05-06 16:47:06 -05:00
{
2009-08-23 22:20:34 -05:00
switch ( getPowerType ())
{
case POWER_FOCUS :
Regenerate ( POWER_FOCUS );
2010-01-10 01:23:15 +01:00
m_regenTimer += PET_FOCUS_REGEN_INTERVAL - diff ;
2009-08-23 22:36:51 -05:00
if ( ! m_regenTimer ) ++ m_regenTimer ;
2009-08-23 22:20:34 -05:00
break ;
2009-08-24 11:35:01 -05:00
// in creature::update
//case POWER_ENERGY:
// Regenerate(POWER_ENERGY);
2010-01-10 01:23:15 +01:00
// m_regenTimer += CREATURE_REGEN_INTERVAL - diff;
2009-08-24 11:35:01 -05:00
// if(!m_regenTimer) ++m_regenTimer;
// break;
2009-08-23 22:20:34 -05:00
default :
m_regenTimer = 0 ;
break ;
}
2009-10-17 16:20:24 -07:00
}
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2009-05-06 16:47:06 -05:00
if ( getPetType () != HUNTER_PET )
break ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
if ( m_happinessTimer <= diff )
{
LooseHappiness ();
m_happinessTimer = 7500 ;
}
else
m_happinessTimer -= diff ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
break ;
}
default :
break ;
}
Creature :: Update ( diff );
}
2009-10-17 15:51:44 -07:00
2009-08-23 22:36:51 -05:00
void Creature :: Regenerate ( Powers power )
2008-11-17 22:07:21 -06:00
{
2009-05-06 16:47:06 -05:00
uint32 curValue = GetPower ( power );
uint32 maxValue = GetMaxPower ( power );
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
if ( curValue >= maxValue )
return ;
2009-10-17 15:51:44 -07:00
2009-05-06 16:47:06 -05:00
float addvalue = 0.0f ;
2009-10-17 15:51:44 -07:00
2009-05-06 16:47:06 -05:00
switch ( power )
{
case POWER_FOCUS :
{
// For hunter pets.
addvalue = 24 * sWorld . getRate ( RATE_POWER_FOCUS );
break ;
}
case POWER_ENERGY :
{
// For deathknight's ghoul.
addvalue = 20 ;
break ;
}
default :
return ;
}
2009-10-17 15:51:44 -07:00
2009-05-06 16:47:06 -05:00
// Apply modifiers (if any).
2010-01-10 01:23:15 +01:00
AuraEffectList const & ModPowerRegenPCTAuras = GetAuraEffectsByType ( SPELL_AURA_MOD_POWER_REGEN_PERCENT );
2009-10-17 16:20:24 -07:00
for ( AuraEffectList :: const_iterator i = ModPowerRegenPCTAuras . begin (); i != ModPowerRegenPCTAuras . end (); ++ i )
2009-05-06 16:47:06 -05:00
if (( * i ) -> GetMiscValue () == power )
2009-04-06 13:31:14 +02:00
addvalue *= (( * i ) -> GetAmount () + 100 ) / 100.0f ;
2009-10-17 15:51:44 -07:00
2010-01-10 01:23:15 +01:00
addvalue += GetTotalAuraModifierByMiscValue ( SPELL_AURA_MOD_POWER_REGEN , power ) * ( isHunterPet () ? PET_FOCUS_REGEN_INTERVAL : CREATURE_REGEN_INTERVAL ) / ( 5 * IN_MILISECONDS );
2009-05-06 16:47:06 -05:00
ModifyPower ( power , ( int32 ) addvalue );
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
void Pet :: LooseHappiness ()
{
uint32 curValue = GetPower ( POWER_HAPPINESS );
if ( curValue <= 0 )
return ;
2008-12-24 09:58:26 -06:00
int32 addvalue = 670 ; //value is 70/35/17/8/4 (per min) * 1000 / 8 (timer 7.5 secs)
2008-11-17 22:07:21 -06:00
if ( isInCombat ()) //we know in combat happiness fades faster, multiplier guess
addvalue = int32 ( addvalue * 1.5 );
ModifyPower ( POWER_HAPPINESS , - addvalue );
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
HappinessState Pet :: GetHappinessState ()
{
if ( GetPower ( POWER_HAPPINESS ) < HAPPINESS_LEVEL_SIZE )
return UNHAPPY ;
else if ( GetPower ( POWER_HAPPINESS ) >= HAPPINESS_LEVEL_SIZE * 2 )
return HAPPY ;
else
return CONTENT ;
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
bool Pet :: CanTakeMoreActiveSpells ( uint32 spellid )
{
uint8 activecount = 1 ;
uint32 chainstartstore [ ACTIVE_SPELLS_MAX ];
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
if ( IsPassiveSpell ( spellid ))
return true ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
chainstartstore [ 0 ] = spellmgr . GetFirstSpellInChain ( spellid );
2009-10-17 15:51:44 -07:00
2009-04-29 00:26:07 -05:00
for ( PetSpellMap :: const_iterator itr = m_spells . begin (); itr != m_spells . end (); ++ itr )
2008-11-17 22:07:21 -06:00
{
2009-05-17 10:57:55 -05:00
if ( itr -> second . state == PETSPELL_REMOVED )
continue ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
if ( IsPassiveSpell ( itr -> first ))
continue ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
uint32 chainstart = spellmgr . GetFirstSpellInChain ( itr -> first );
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
uint8 x ;
2009-10-17 15:51:44 -07:00
2009-10-17 16:20:24 -07:00
for ( x = 0 ; x < activecount ; x ++ )
2008-11-17 22:07:21 -06:00
{
if ( chainstart == chainstartstore [ x ])
break ;
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
if ( x == activecount ) //spellchain not yet saved -> add active count
{
++ activecount ;
if ( activecount > ACTIVE_SPELLS_MAX )
return false ;
chainstartstore [ x ] = chainstart ;
}
}
return true ;
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
void Pet :: Remove ( PetSaveMode mode , bool returnreagent )
{
2009-03-27 22:47:28 -06:00
m_owner -> RemovePet ( this , mode , returnreagent );
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
void Pet :: GivePetXP ( uint32 xp )
{
if ( getPetType () != HUNTER_PET )
return ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
if ( xp < 1 )
return ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
if ( ! isAlive ())
return ;
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
uint8 level = getLevel ();
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
// XP to money conversion processed in Player::RewardQuest
if ( level >= sWorld . getConfig ( CONFIG_MAX_PLAYER_LEVEL ))
return ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
uint32 curXP = GetUInt32Value ( UNIT_FIELD_PETEXPERIENCE );
uint32 nextLvlXP = GetUInt32Value ( UNIT_FIELD_PETNEXTLEVELEXP );
uint32 newXP = curXP + xp ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
if ( newXP >= nextLvlXP && level + 1 > GetOwner () -> getLevel ())
{
SetUInt32Value ( UNIT_FIELD_PETEXPERIENCE , nextLvlXP - 1 );
return ;
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
while ( newXP >= nextLvlXP && level < sWorld . getConfig ( CONFIG_MAX_PLAYER_LEVEL ) )
{
newXP -= nextLvlXP ;
2009-10-17 15:51:44 -07:00
2009-05-15 13:44:49 -05:00
GivePetLevel ( level + 1 );
2009-03-28 12:39:14 -06:00
SetUInt32Value ( UNIT_FIELD_PETNEXTLEVELEXP , objmgr . GetXPForLevel ( level + 1 ) * PET_XP_FACTOR );
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
level = getLevel ();
nextLvlXP = GetUInt32Value ( UNIT_FIELD_PETNEXTLEVELEXP );
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
SetUInt32Value ( UNIT_FIELD_PETEXPERIENCE , newXP );
}
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
void Pet :: GivePetLevel ( uint8 level )
2008-11-17 22:07:21 -06:00
{
2009-11-01 17:53:07 -08:00
if ( ! level )
2008-11-17 22:07:21 -06:00
return ;
2009-10-17 15:51:44 -07:00
2008-12-24 09:58:26 -06:00
InitStatsForLevel ( level );
2009-05-17 10:57:55 -05:00
InitLevelupSpellsForLevel ();
2009-01-20 19:59:43 -06:00
InitTalentForLevel ();
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
bool Pet :: CreateBaseAtCreature ( Creature * creature )
{
if ( ! creature )
{
2009-03-21 14:28:02 -06:00
sLog . outError ( "CRITICAL: NULL pointer parsed into CreateBaseAtCreature()" );
2008-11-17 22:07:21 -06:00
return false ;
}
uint32 guid = objmgr . GenerateLowGuid ( HIGHGUID_PET );
2009-10-17 15:51:44 -07:00
2009-01-15 22:01:47 -05:00
sLog . outDebug ( "Create pet" );
2008-11-17 22:07:21 -06:00
uint32 pet_number = objmgr . GeneratePetNumber ();
2009-01-31 16:38:50 -06:00
if ( ! Create ( guid , creature -> GetMap (), creature -> GetPhaseMask (), creature -> GetEntry (), pet_number ))
2008-11-17 22:07:21 -06:00
return false ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
Relocate ( creature -> GetPositionX (), creature -> GetPositionY (), creature -> GetPositionZ (), creature -> GetOrientation ());
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
if ( ! IsPositionValid ())
{
2009-03-21 14:28:02 -06:00
sLog . outError ( "Pet (guidlow %d, entry %d) not created base at creature. Suggested coordinates isn't valid (X: %f Y: %f)" ,
2008-11-17 22:07:21 -06:00
GetGUIDLow (), GetEntry (), GetPositionX (), GetPositionY ());
return false ;
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
CreatureInfo const * cinfo = GetCreatureInfo ();
if ( ! cinfo )
{
2009-03-21 14:28:02 -06:00
sLog . outError ( "CreateBaseAtCreature() failed, creatureInfo is missing!" );
2008-11-17 22:07:21 -06:00
return false ;
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
SetDisplayId ( creature -> GetDisplayId ());
SetNativeDisplayId ( creature -> GetNativeDisplayId ());
2008-12-12 11:21:28 -06:00
SetMaxPower ( POWER_HAPPINESS , GetCreatePowers ( POWER_HAPPINESS ));
SetPower ( POWER_HAPPINESS , 166500 );
2008-11-17 22:07:21 -06:00
setPowerType ( POWER_FOCUS );
2008-12-12 11:21:28 -06:00
SetUInt32Value ( UNIT_FIELD_PET_NAME_TIMESTAMP , 0 );
SetUInt32Value ( UNIT_FIELD_PETEXPERIENCE , 0 );
2009-03-28 12:39:14 -06:00
SetUInt32Value ( UNIT_FIELD_PETNEXTLEVELEXP , objmgr . GetXPForLevel ( creature -> getLevel ()) * PET_XP_FACTOR );
2009-06-03 19:29:55 -05:00
SetUInt32Value ( UNIT_NPC_FLAGS , UNIT_NPC_FLAG_NONE );
2009-10-17 15:51:44 -07:00
2009-01-22 22:44:27 -06:00
if ( CreatureFamilyEntry const * cFamily = sCreatureFamilyStore . LookupEntry ( cinfo -> family ))
SetName ( cFamily -> Name [ sWorld . GetDefaultDbcLocale ()]);
2008-11-17 22:07:21 -06:00
else
2009-01-22 22:44:27 -06:00
SetName ( creature -> GetNameForLocaleIdx ( objmgr . GetDBCLocaleIndex ()));
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
if ( cinfo -> type == CREATURE_TYPE_BEAST )
{
SetUInt32Value ( UNIT_FIELD_BYTES_0 , 0x02020100 );
2009-05-28 18:43:57 -05:00
SetSheath ( SHEATH_STATE_MELEE );
2010-02-14 19:13:14 -07:00
SetByteFlag ( UNIT_FIELD_BYTES_2 , 2 , UNIT_CAN_BE_RENAMED | UNIT_CAN_BE_ABANDONED );
2008-12-24 09:58:26 -06:00
SetUInt32Value ( UNIT_MOD_CAST_SPEED , creature -> GetUInt32Value ( UNIT_MOD_CAST_SPEED ));
2008-11-17 22:07:21 -06:00
}
return true ;
}
2009-10-17 15:51:44 -07:00
2009-06-18 14:17:11 +02:00
// TODO: Move stat mods code to pet passive auras
2009-11-01 17:53:07 -08:00
bool Guardian :: InitStatsForLevel ( uint8 petlevel )
2008-11-17 22:07:21 -06:00
{
CreatureInfo const * cinfo = GetCreatureInfo ();
assert ( cinfo );
2009-10-17 15:51:44 -07:00
2009-03-25 21:32:34 -06:00
SetLevel ( petlevel );
2009-10-17 15:51:44 -07:00
2009-04-15 19:13:33 -05:00
//Determine pet type
2009-03-25 21:32:34 -06:00
PetType petType = MAX_PET_TYPE ;
2009-08-26 19:28:21 -05:00
if ( isPet () && m_owner -> GetTypeId () == TYPEID_PLAYER )
2008-11-17 22:07:21 -06:00
{
2010-01-01 19:37:52 -06:00
if (( m_owner -> getClass () == CLASS_WARLOCK )
|| ( m_owner -> getClass () == CLASS_SHAMAN ) // Fire Elemental
|| ( m_owner -> getClass () == CLASS_DEATH_KNIGHT )) // Risen Ghoul
2009-03-25 21:32:34 -06:00
petType = SUMMON_PET ;
else if ( m_owner -> getClass () == CLASS_HUNTER )
2009-06-02 10:34:32 -05:00
{
2009-03-25 21:32:34 -06:00
petType = HUNTER_PET ;
2009-08-23 22:09:43 -05:00
m_unitTypeMask |= UNIT_MASK_HUNTER_PET ;
2009-06-02 10:34:32 -05:00
}
2009-03-25 21:32:34 -06:00
else
sLog . outError ( "Unknown type pet %u is summoned by player class %u" , GetEntry (), m_owner -> getClass ());
}
2009-10-17 15:51:44 -07:00
2009-03-25 21:32:34 -06:00
uint32 creature_ID = ( petType == HUNTER_PET ) ? 1 : cinfo -> Entry ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
SetMeleeDamageSchool ( SpellSchools ( cinfo -> dmgschool ));
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
SetModifierValue ( UNIT_MOD_ARMOR , BASE_VALUE , float ( petlevel * 50 ));
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
SetAttackTime ( BASE_ATTACK , BASE_ATTACK_TIME );
SetAttackTime ( OFF_ATTACK , BASE_ATTACK_TIME );
SetAttackTime ( RANGED_ATTACK , BASE_ATTACK_TIME );
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
SetFloatValue ( UNIT_MOD_CAST_SPEED , 1.0 );
2009-10-17 15:51:44 -07:00
2009-04-15 19:13:33 -05:00
//scale
2008-11-17 22:07:21 -06:00
CreatureFamilyEntry const * cFamily = sCreatureFamilyStore . LookupEntry ( cinfo -> family );
2009-03-25 21:32:34 -06:00
if ( cFamily && cFamily -> minScale > 0.0f && petType == HUNTER_PET )
2008-11-17 22:07:21 -06:00
{
float scale ;
if ( getLevel () >= cFamily -> maxScaleLevel )
scale = cFamily -> maxScale ;
else if ( getLevel () <= cFamily -> minScaleLevel )
scale = cFamily -> minScale ;
else
2009-05-17 10:58:31 -05:00
scale = cFamily -> minScale + float ( getLevel () - cFamily -> minScaleLevel ) / cFamily -> maxScaleLevel * ( cFamily -> maxScale - cFamily -> minScale );
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
SetFloatValue ( OBJECT_FIELD_SCALE_X , scale );
}
2009-10-17 15:51:44 -07:00
2009-04-15 19:13:33 -05:00
//resistance
2008-11-17 22:07:21 -06:00
int32 createResistance [ MAX_SPELL_SCHOOL ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 };
2009-11-01 17:53:07 -08:00
if ( cinfo && petType != HUNTER_PET )
2008-11-17 22:07:21 -06:00
{
createResistance [ SPELL_SCHOOL_HOLY ] = cinfo -> resistance1 ;
createResistance [ SPELL_SCHOOL_FIRE ] = cinfo -> resistance2 ;
createResistance [ SPELL_SCHOOL_NATURE ] = cinfo -> resistance3 ;
createResistance [ SPELL_SCHOOL_FROST ] = cinfo -> resistance4 ;
createResistance [ SPELL_SCHOOL_SHADOW ] = cinfo -> resistance5 ;
createResistance [ SPELL_SCHOOL_ARCANE ] = cinfo -> resistance6 ;
}
2009-06-18 18:29:28 +02:00
for ( uint8 i = SPELL_SCHOOL_HOLY ; i < MAX_SPELL_SCHOOL ; ++ i )
2009-04-15 19:13:33 -05:00
SetModifierValue ( UnitMods ( UNIT_MOD_RESISTANCE_START + i ), BASE_VALUE , float ( createResistance [ i ]));
2009-10-17 15:51:44 -07:00
2009-04-15 19:13:33 -05:00
//health, mana, armor and resistance
PetLevelInfo const * pInfo = objmgr . GetPetLevelInfo ( creature_ID , petlevel );
2009-11-01 17:53:07 -08:00
if ( pInfo ) // exist in DB
2009-04-15 19:13:33 -05:00
{
SetCreateHealth ( pInfo -> health );
2009-11-01 17:53:07 -08:00
if ( petType != HUNTER_PET ) //hunter pet use focus
2009-04-15 19:13:33 -05:00
SetCreateMana ( pInfo -> mana );
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( pInfo -> armor > 0 )
2009-04-15 19:13:33 -05:00
SetModifierValue ( UNIT_MOD_ARMOR , BASE_VALUE , float ( pInfo -> armor ));
2009-10-17 15:51:44 -07:00
2009-10-17 16:20:24 -07:00
for ( uint8 stat = 0 ; stat < MAX_STATS ; ++ stat )
2009-04-15 19:13:33 -05:00
SetCreateStat ( Stats ( stat ), float ( pInfo -> stats [ stat ]));
}
else // not exist in DB, use some default fake data
{
// remove elite bonuses included in DB values
//SetCreateHealth(uint32(((float(cinfo->maxhealth) / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) );
//SetCreateMana( uint32(((float(cinfo->maxmana) / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) );
2009-10-17 15:51:44 -07:00
2009-04-15 19:13:33 -05:00
SetCreateStat ( STAT_STRENGTH , 22 );
SetCreateStat ( STAT_AGILITY , 22 );
SetCreateStat ( STAT_STAMINA , 25 );
SetCreateStat ( STAT_INTELLECT , 28 );
SetCreateStat ( STAT_SPIRIT , 27 );
}
2009-12-26 10:24:19 +01:00
if ( GetEntry () == 29264 )
{
float apbonus = 0.35f ;
2010-01-27 14:56:14 -03:00
2009-12-26 10:24:19 +01:00
// Glyph of Feral Spirit
if ( AuraEffect * aurEff = m_owner -> GetAuraEffect ( 63271 , 0 ))
apbonus += aurEff -> GetAmount () / 100.0f ;
}
2009-04-15 19:13:33 -05:00
m_bonusdamage = 0 ;
2009-11-01 17:53:07 -08:00
switch ( petType )
2008-11-17 22:07:21 -06:00
{
case SUMMON_PET :
{
2009-03-25 21:32:34 -06:00
//the damage bonus used for pets is either fire or shadow damage, whatever is higher
uint32 fire = m_owner -> GetUInt32Value ( PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FIRE );
uint32 shadow = m_owner -> GetUInt32Value ( PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_SHADOW );
uint32 val = ( fire > shadow ) ? fire : shadow ;
SetBonusDamage ( int32 ( val * 0.15f ));
//bonusAP += val * 0.57;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
SetBaseWeaponDamage ( BASE_ATTACK , MINDAMAGE , float ( petlevel - ( petlevel / 4 )) );
SetBaseWeaponDamage ( BASE_ATTACK , MAXDAMAGE , float ( petlevel + ( petlevel / 4 )) );
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
//SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, float(cinfo->attackpower));
break ;
}
case HUNTER_PET :
{
2009-03-28 12:39:14 -06:00
SetUInt32Value ( UNIT_FIELD_PETNEXTLEVELEXP , objmgr . GetXPForLevel ( petlevel ) * PET_XP_FACTOR );
2008-11-17 22:07:21 -06:00
//these formula may not be correct; however, it is designed to be close to what it should be
//this makes dps 0.5 of pets level
SetBaseWeaponDamage ( BASE_ATTACK , MINDAMAGE , float ( petlevel - ( petlevel / 4 )) );
//damage range is then petlevel / 2
SetBaseWeaponDamage ( BASE_ATTACK , MAXDAMAGE , float ( petlevel + ( petlevel / 4 )) );
//damage is increased afterwards as strength and pet scaling modify attack power
break ;
}
default :
2009-04-15 19:13:33 -05:00
{
2009-11-01 17:53:07 -08:00
switch ( GetEntry ())
2009-03-25 21:32:34 -06:00
{
case 510 : // mage Water Elemental
{
//40% damage bonus of mage's frost damage
float val = m_owner -> GetUInt32Value ( PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FROST ) * 0.4 ;
if ( val < 0 )
val = 0 ;
SetBonusDamage ( int32 ( val ));
break ;
}
case 1964 : //force of nature
2009-04-15 19:13:33 -05:00
{
2009-11-01 17:53:07 -08:00
if ( ! pInfo )
2009-04-15 19:13:33 -05:00
SetCreateHealth ( 30 + 30 * petlevel );
2009-03-25 21:32:34 -06:00
SetBaseWeaponDamage ( BASE_ATTACK , MINDAMAGE , float ( petlevel * 2.5f - ( petlevel / 2 )));
SetBaseWeaponDamage ( BASE_ATTACK , MAXDAMAGE , float ( petlevel * 2.5f + ( petlevel / 2 )));
break ;
2009-04-15 19:13:33 -05:00
}
2009-03-25 21:32:34 -06:00
case 15352 : //earth elemental 36213
2009-04-15 19:13:33 -05:00
{
2009-11-01 17:53:07 -08:00
if ( ! pInfo )
2009-04-15 19:13:33 -05:00
SetCreateHealth ( 100 + 120 * petlevel );
2009-03-25 21:32:34 -06:00
SetBaseWeaponDamage ( BASE_ATTACK , MINDAMAGE , float ( petlevel - ( petlevel / 4 )));
SetBaseWeaponDamage ( BASE_ATTACK , MAXDAMAGE , float ( petlevel + ( petlevel / 4 )));
break ;
2009-04-15 19:13:33 -05:00
}
2009-03-25 21:32:34 -06:00
case 15438 : //fire elemental
2009-04-15 19:13:33 -05:00
{
2009-11-01 17:53:07 -08:00
if ( ! pInfo )
2009-04-15 19:13:33 -05:00
{
SetCreateHealth ( 40 * petlevel );
SetCreateMana ( 28 + 10 * petlevel );
}
2009-03-25 21:32:34 -06:00
SetBaseWeaponDamage ( BASE_ATTACK , MINDAMAGE , float ( petlevel * 4 - petlevel ));
SetBaseWeaponDamage ( BASE_ATTACK , MAXDAMAGE , float ( petlevel * 4 + petlevel ));
break ;
2009-04-15 19:13:33 -05:00
}
2010-01-13 18:24:23 +01:00
case 19833 : //Snake Trap - Venomous Snake
{
SetCreateHealth ( uint32 ( 107 * ( petlevel - 40 ) * 0.025f ));
SetCreateMana ( 0 );
SetBaseWeaponDamage ( BASE_ATTACK , MINDAMAGE , float (( petlevel / 2 ) - 25 ));
SetBaseWeaponDamage ( BASE_ATTACK , MAXDAMAGE , float (( petlevel / 2 ) - 18 ));
break ;
}
case 19921 : //Snake Trap - Viper
{
SetCreateHealth ( uint32 ( 107 * ( petlevel - 40 ) * 0.025f ));
SetCreateMana ( 0 );
SetBaseWeaponDamage ( BASE_ATTACK , MINDAMAGE , float ( petlevel / 2 - 10 ));
SetBaseWeaponDamage ( BASE_ATTACK , MAXDAMAGE , float ( petlevel / 2 ));
break ;
}
2010-01-27 14:56:14 -03:00
case 29264 : // Feral Spirit
{
if ( ! pInfo )
SetCreateHealth ( 30 * petlevel );
2010-01-27 15:15:49 -03:00
float dmg_multiplier = 0.3f ;
2010-01-27 14:56:14 -03:00
if ( HasAura ( 63271 )) // Glyph of Feral Spirit
dmg_multiplier = 0.6f ;
SetBonusDamage ( int32 ( m_owner -> GetTotalAttackPowerValue ( BASE_ATTACK ) * dmg_multiplier ));
SetBaseWeaponDamage ( BASE_ATTACK , MINDAMAGE , float (( petlevel * 4 - petlevel ) + ( m_owner -> GetTotalAttackPowerValue ( BASE_ATTACK ) * dmg_multiplier )));
SetBaseWeaponDamage ( BASE_ATTACK , MAXDAMAGE , float (( petlevel * 4 + petlevel ) + ( m_owner -> GetTotalAttackPowerValue ( BASE_ATTACK ) * dmg_multiplier )));
SetModifierValue ( UNIT_MOD_ARMOR , BASE_VALUE , float ( m_owner -> GetArmor ()) * 0.35f ); // Bonus Armor (35% of player armor)
SetModifierValue ( UNIT_MOD_STAT_STAMINA , BASE_VALUE , float ( m_owner -> GetStat ( STAT_STAMINA )) * 0.3f ); // Bonus Stamina (30% of player stamina)
break ;
}
2009-07-28 21:34:18 +02:00
case 31216 : // Mirror Image
{
2009-11-01 17:53:07 -08:00
SetBonusDamage ( int32 ( m_owner -> SpellBaseDamageBonus ( SPELL_SCHOOL_MASK_FROST ) * 0.33f ));
2009-12-17 06:52:42 +01:00
SetDisplayId ( m_owner -> GetDisplayId ());
2009-11-01 17:53:07 -08:00
if ( ! pInfo )
2009-07-28 21:34:18 +02:00
{
SetCreateMana ( 28 + 30 * petlevel );
SetCreateHealth ( 28 + 10 * petlevel );
}
break ;
}
2009-08-14 15:30:47 +02:00
case 27829 : // Ebon Gargoyle
{
2009-11-01 17:53:07 -08:00
if ( ! pInfo )
2009-08-14 15:30:47 +02:00
{
SetCreateMana ( 28 + 10 * petlevel );
SetCreateHealth ( 28 + 30 * petlevel );
}
2009-12-17 09:10:04 +01:00
SetBonusDamage ( int32 ( m_owner -> GetTotalAttackPowerValue ( BASE_ATTACK ) * 0.5f ));
2009-03-25 21:32:34 -06:00
SetBaseWeaponDamage ( BASE_ATTACK , MINDAMAGE , float ( petlevel - ( petlevel / 4 )));
SetBaseWeaponDamage ( BASE_ATTACK , MAXDAMAGE , float ( petlevel + ( petlevel / 4 )));
break ;
2009-04-15 19:13:33 -05:00
}
2009-03-25 21:32:34 -06:00
}
2008-12-12 11:21:28 -06:00
break ;
2009-04-15 19:13:33 -05:00
}
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
UpdateAllStats ();
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
SetHealth ( GetMaxHealth ());
SetPower ( POWER_MANA , GetMaxPower ( POWER_MANA ));
return true ;
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
bool Pet :: HaveInDiet ( ItemPrototype const * item ) const
{
if ( ! item -> FoodType )
return false ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
CreatureInfo const * cInfo = GetCreatureInfo ();
2009-11-01 17:53:07 -08:00
if ( ! cInfo )
2008-11-17 22:07:21 -06:00
return false ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
CreatureFamilyEntry const * cFamily = sCreatureFamilyStore . LookupEntry ( cInfo -> family );
2009-11-01 17:53:07 -08:00
if ( ! cFamily )
2008-11-17 22:07:21 -06:00
return false ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
uint32 diet = cFamily -> petFoodMask ;
uint32 FoodMask = 1 << ( item -> FoodType - 1 );
return diet & FoodMask ;
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
uint32 Pet :: GetCurrentFoodBenefitLevel ( uint32 itemlevel )
{
// -5 or greater food level
2008-12-12 11:21:28 -06:00
if ( getLevel () <= itemlevel + 5 ) //possible to feed level 60 pet with level 55 level food for full effect
2008-11-17 22:07:21 -06:00
return 35000 ;
// -10..-6
else if ( getLevel () <= itemlevel + 10 ) //pure guess, but sounds good
return 17000 ;
// -14..-11
else if ( getLevel () <= itemlevel + 14 ) //level 55 food gets green on 70, makes sense to me
return 8000 ;
// -15 or less
else
return 0 ; //food too low level
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
void Pet :: _LoadSpellCooldowns ()
{
m_CreatureSpellCooldowns . clear ();
m_CreatureCategoryCooldowns . clear ();
2009-10-17 15:51:44 -07:00
2010-01-23 14:45:58 +01:00
QueryResult_AutoPtr result = CharacterDatabase . PQuery ( "SELECT spell,time FROM pet_spell_cooldown WHERE guid = '%u'" , m_charmInfo -> GetPetNumber ());
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
if ( result )
{
2009-12-24 10:20:15 +01:00
time_t curTime = time ( NULL );
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
WorldPacket data ( SMSG_SPELL_COOLDOWN , ( 8 + 1 + result -> GetRowCount () * 8 ));
data << GetGUID ();
data << uint8 ( 0x0 ); // flags (0x1, 0x2)
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
do
{
Field * fields = result -> Fetch ();
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
uint32 spell_id = fields [ 0 ]. GetUInt32 ();
time_t db_time = ( time_t ) fields [ 1 ]. GetUInt64 ();
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
if ( ! sSpellStore . LookupEntry ( spell_id ))
{
sLog . outError ( "Pet %u have unknown spell %u in `pet_spell_cooldown`, skipping." , m_charmInfo -> GetPetNumber (), spell_id );
continue ;
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
// skip outdated cooldown
if ( db_time <= curTime )
continue ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
data << uint32 ( spell_id );
2009-03-02 16:53:50 -06:00
data << uint32 ( uint32 ( db_time - curTime ) * IN_MILISECONDS );
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
_AddCreatureSpellCooldown ( spell_id , db_time );
2009-10-17 15:51:44 -07:00
2008-12-12 11:21:28 -06:00
sLog . outDebug ( "Pet (Number: %u) spell %u cooldown loaded (%u secs)." , m_charmInfo -> GetPetNumber (), spell_id , uint32 ( db_time - curTime ));
2008-11-17 22:07:21 -06:00
}
2009-11-01 17:53:07 -08:00
while ( result -> NextRow ());
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
if ( ! m_CreatureSpellCooldowns . empty () && GetOwner ())
(( Player * ) GetOwner ()) -> GetSession () -> SendPacket ( & data );
}
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
void Pet :: _SaveSpellCooldowns ()
{
CharacterDatabase . PExecute ( "DELETE FROM pet_spell_cooldown WHERE guid = '%u'" , m_charmInfo -> GetPetNumber ());
2009-10-17 15:51:44 -07:00
2009-12-24 10:20:15 +01:00
time_t curTime = time ( NULL );
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
// remove oudated and save active
2009-11-20 19:11:28 -08:00
for ( CreatureSpellCooldowns :: iterator itr = m_CreatureSpellCooldowns . begin (); itr != m_CreatureSpellCooldowns . end ();)
2008-11-17 22:07:21 -06:00
{
2009-11-01 17:53:07 -08:00
if ( itr -> second <= curTime )
2008-11-17 22:07:21 -06:00
m_CreatureSpellCooldowns . erase ( itr ++ );
else
{
2009-05-31 16:15:12 -05:00
CharacterDatabase . PExecute ( "INSERT INTO pet_spell_cooldown (guid,spell,time) VALUES ('%u', '%u', '" UI64FMTD "')" , m_charmInfo -> GetPetNumber (), itr -> first , uint64 ( itr -> second ));
2008-11-17 22:07:21 -06:00
++ itr ;
}
}
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
void Pet :: _LoadSpells ()
{
2010-01-23 14:45:58 +01:00
QueryResult_AutoPtr result = CharacterDatabase . PQuery ( "SELECT spell,active FROM pet_spell WHERE guid = '%u'" , m_charmInfo -> GetPetNumber ());
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( result )
2008-11-17 22:07:21 -06:00
{
do
{
Field * fields = result -> Fetch ();
2009-10-17 15:51:44 -07:00
2009-07-01 18:07:20 -05:00
addSpell ( fields [ 0 ]. GetUInt32 (), ActiveStates ( fields [ 1 ]. GetUInt8 ()), PETSPELL_UNCHANGED );
2008-11-17 22:07:21 -06:00
}
2009-11-01 17:53:07 -08:00
while ( result -> NextRow ());
2008-11-17 22:07:21 -06:00
}
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
void Pet :: _SaveSpells ()
{
2009-05-15 16:46:02 -05:00
for ( PetSpellMap :: iterator itr = m_spells . begin (), next = m_spells . begin (); itr != m_spells . end (); itr = next )
2008-11-17 22:07:21 -06:00
{
++ next ;
2009-10-17 15:51:44 -07:00
2009-06-02 17:47:54 -05:00
// prevent saving family passives to DB
if ( itr -> second . type == PETSPELL_FAMILY )
2009-05-15 16:46:02 -05:00
continue ;
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
switch ( itr -> second . state )
2009-05-15 16:46:02 -05:00
{
case PETSPELL_REMOVED :
CharacterDatabase . PExecute ( "DELETE FROM pet_spell WHERE guid = '%u' and spell = '%u'" , m_charmInfo -> GetPetNumber (), itr -> first );
m_spells . erase ( itr );
continue ;
case PETSPELL_CHANGED :
CharacterDatabase . PExecute ( "DELETE FROM pet_spell WHERE guid = '%u' and spell = '%u'" , m_charmInfo -> GetPetNumber (), itr -> first );
CharacterDatabase . PExecute ( "INSERT INTO pet_spell (guid,spell,active) VALUES ('%u', '%u', '%u')" , m_charmInfo -> GetPetNumber (), itr -> first , itr -> second . active );
break ;
case PETSPELL_NEW :
CharacterDatabase . PExecute ( "INSERT INTO pet_spell (guid,spell,active) VALUES ('%u', '%u', '%u')" , m_charmInfo -> GetPetNumber (), itr -> first , itr -> second . active );
break ;
case PETSPELL_UNCHANGED :
continue ;
}
2009-10-17 15:51:44 -07:00
2009-05-15 16:46:02 -05:00
itr -> second . state = PETSPELL_UNCHANGED ;
2008-11-17 22:07:21 -06:00
}
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
void Pet :: _LoadAuras ( uint32 timediff )
{
2009-04-06 13:31:14 +02:00
sLog . outDebug ( "Loading auras for pet %u" , GetGUIDLow ());
2009-10-17 15:51:44 -07:00
2010-01-23 14:45:58 +01:00
QueryResult_AutoPtr result = CharacterDatabase . PQuery ( "SELECT caster_guid,spell,effect_mask,recalculate_mask,stackcount,amount0,amount1,amount2,base_amount0,base_amount1,base_amount2,maxduration,remaintime,remaincharges FROM pet_aura WHERE guid = '%u'" , m_charmInfo -> GetPetNumber ());
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( result )
2008-11-17 22:07:21 -06:00
{
do
{
2009-04-06 13:31:14 +02:00
int32 damage [ 3 ];
2010-01-10 01:23:15 +01:00
int32 baseDamage [ 3 ];
2008-11-17 22:07:21 -06:00
Field * fields = result -> Fetch ();
uint64 caster_guid = fields [ 0 ]. GetUInt64 ();
uint32 spellid = fields [ 1 ]. GetUInt32 ();
2009-11-01 17:53:07 -08:00
uint8 effmask = fields [ 2 ]. GetUInt8 ();
2010-01-10 01:23:15 +01:00
uint8 recalculatemask = fields [ 3 ]. GetUInt8 ();
uint8 stackcount = fields [ 4 ]. GetUInt8 ();
damage [ 0 ] = fields [ 5 ]. GetInt32 ();
damage [ 1 ] = fields [ 6 ]. GetInt32 ();
damage [ 2 ] = fields [ 7 ]. GetInt32 ();
baseDamage [ 0 ] = fields [ 8 ]. GetInt32 ();
baseDamage [ 1 ] = fields [ 9 ]. GetInt32 ();
baseDamage [ 2 ] = fields [ 10 ]. GetInt32 ();
int32 maxduration = fields [ 11 ]. GetInt32 ();
int32 remaintime = fields [ 12 ]. GetInt32 ();
uint8 remaincharges = fields [ 13 ]. GetUInt8 ();
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
SpellEntry const * spellproto = sSpellStore . LookupEntry ( spellid );
if ( ! spellproto )
{
2009-04-06 13:31:14 +02:00
sLog . outError ( "Unknown aura (spellid %u), ignore." , spellid );
2008-11-17 22:07:21 -06:00
continue ;
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
// negative effects should continue counting down after logout
2009-04-06 13:31:14 +02:00
if ( remaintime != - 1 && ! IsPositiveSpell ( spellid ))
2008-11-17 22:07:21 -06:00
{
2009-10-31 16:44:14 -07:00
if ( remaintime / IN_MILISECONDS <= int32 ( timediff ))
2008-11-17 22:07:21 -06:00
continue ;
2009-10-17 15:51:44 -07:00
2009-10-31 16:44:14 -07:00
remaintime -= timediff * IN_MILISECONDS ;
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
// prevent wrong values of remaincharges
if ( spellproto -> procCharges )
{
if ( remaincharges <= 0 || remaincharges > spellproto -> procCharges )
remaincharges = spellproto -> procCharges ;
}
else
2009-04-06 13:31:14 +02:00
remaincharges = 0 ;
2009-10-17 15:51:44 -07:00
2010-01-10 01:23:15 +01:00
if ( Aura * aura = Aura :: TryCreate ( spellproto , effmask , this , NULL , & baseDamage [ 0 ], NULL , caster_guid ))
2009-08-11 18:26:04 -05:00
{
2010-01-10 01:23:15 +01:00
if ( ! aura -> CanBeSaved ())
{
aura -> Remove ();
continue ;
}
aura -> SetLoadedState ( maxduration , remaintime , remaincharges , stackcount , recalculatemask , & damage [ 0 ]);
aura -> ApplyForTargets ();
sLog . outDetail ( "Added aura spellid %u, effectmask %u" , spellproto -> Id , effmask );
2009-08-11 18:26:04 -05:00
}
2008-11-17 22:07:21 -06:00
}
2009-11-01 17:53:07 -08:00
while ( result -> NextRow ());
2008-11-17 22:07:21 -06:00
}
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
void Pet :: _SaveAuras ()
{
2008-12-12 11:21:28 -06:00
CharacterDatabase . PExecute ( "DELETE FROM pet_aura WHERE guid = '%u'" , m_charmInfo -> GetPetNumber ());
2009-10-17 15:51:44 -07:00
2010-01-10 01:23:15 +01:00
for ( AuraMap :: const_iterator itr = m_ownedAuras . begin (); itr != m_ownedAuras . end () ; ++ itr )
2008-11-20 17:40:13 -06:00
{
2009-11-01 17:53:07 -08:00
if ( ! itr -> second -> CanBeSaved ())
2009-04-06 13:31:14 +02:00
continue ;
2009-10-17 15:51:44 -07:00
2010-01-10 01:23:15 +01:00
Aura * aura = itr -> second ;
int32 damage [ MAX_SPELL_EFFECTS ];
int32 baseDamage [ MAX_SPELL_EFFECTS ];
uint8 effMask = 0 ;
uint8 recalculateMask = 0 ;
2009-05-02 21:49:21 -05:00
for ( uint8 i = 0 ; i < MAX_SPELL_EFFECTS ; ++ i )
2008-11-20 17:40:13 -06:00
{
2010-01-10 01:23:15 +01:00
if ( aura -> GetEffect ( i ))
{
baseDamage [ i ] = aura -> GetEffect ( i ) -> GetBaseAmount ();
damage [ i ] = aura -> GetEffect ( i ) -> GetAmount ();
effMask |= ( 1 << i );
if ( aura -> GetEffect ( i ) -> CanBeRecalculated ())
recalculateMask |= ( 1 << i );
}
2009-04-06 13:31:14 +02:00
else
2010-01-10 01:23:15 +01:00
{
baseDamage [ i ] = NULL ;
damage [ i ] = NULL ;
}
2008-11-20 17:40:13 -06:00
}
2009-10-17 15:51:44 -07:00
2010-01-10 01:23:15 +01:00
CharacterDatabase . PExecute ( "INSERT INTO pet_aura (guid,caster_guid,spell,effect_mask,recalculate_mask,stackcount,amount0,amount1,amount2,base_amount0,base_amount1,base_amount2,maxduration,remaintime,remaincharges) "
"VALUES ('%u', '" UI64FMTD "', '%u', '%u', '%u', '%u', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%u')" ,
2010-01-27 14:56:14 -03:00
m_charmInfo -> GetPetNumber (), itr -> second -> GetCasterGUID (), itr -> second -> GetId (), effMask , recalculateMask ,
2010-01-10 01:23:15 +01:00
itr -> second -> GetStackAmount (), damage [ 0 ], damage [ 1 ], damage [ 2 ], baseDamage [ 0 ], baseDamage [ 1 ], baseDamage [ 2 ],
itr -> second -> GetMaxDuration (), itr -> second -> GetDuration (), itr -> second -> GetCharges ());
2008-11-17 22:07:21 -06:00
}
}
2009-10-17 15:51:44 -07:00
2009-05-17 10:57:55 -05:00
bool Pet :: addSpell ( uint32 spell_id , ActiveStates active /*= ACT_DECIDE*/ , PetSpellState state /*= PETSPELL_NEW*/ , PetSpellType type /*= PETSPELL_NORMAL*/ )
2008-11-17 22:07:21 -06:00
{
SpellEntry const * spellInfo = sSpellStore . LookupEntry ( spell_id );
if ( ! spellInfo )
{
// do pet spell book cleanup
2009-11-01 17:53:07 -08:00
if ( state == PETSPELL_UNCHANGED ) // spell load case
2008-11-17 22:07:21 -06:00
{
sLog . outError ( "Pet::addSpell: Non-existed in SpellStore spell #%u request, deleting for all pets in `pet_spell`." , spell_id );
CharacterDatabase . PExecute ( "DELETE FROM pet_spell WHERE spell = '%u'" , spell_id );
}
else
sLog . outError ( "Pet::addSpell: Non-existed in SpellStore spell #%u request." , spell_id );
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
return false ;
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
PetSpellMap :: iterator itr = m_spells . find ( spell_id );
if ( itr != m_spells . end ())
{
2009-05-15 16:46:02 -05:00
if ( itr -> second . state == PETSPELL_REMOVED )
2008-11-17 22:07:21 -06:00
{
m_spells . erase ( itr );
state = PETSPELL_CHANGED ;
}
2009-05-15 16:46:02 -05:00
else if ( state == PETSPELL_UNCHANGED && itr -> second . state != PETSPELL_UNCHANGED )
2008-11-17 22:07:21 -06:00
{
// can be in case spell loading but learned at some previous spell loading
2009-05-15 16:46:02 -05:00
itr -> second . state = PETSPELL_UNCHANGED ;
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( active == ACT_ENABLED )
2009-05-03 22:29:32 -05:00
ToggleAutocast ( spell_id , true );
2009-11-01 17:53:07 -08:00
else if ( active == ACT_DISABLED )
2009-05-03 22:29:32 -05:00
ToggleAutocast ( spell_id , false );
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
return false ;
}
else
return false ;
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
uint32 oldspell_id = 0 ;
2009-10-17 15:51:44 -07:00
2009-05-15 16:46:02 -05:00
PetSpell newspell ;
newspell . state = state ;
newspell . type = type ;
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( active == ACT_DECIDE ) // active was not used before, so we save it's autocast/passive state here
2008-11-17 22:07:21 -06:00
{
2009-11-01 17:53:07 -08:00
if ( IsAutocastableSpell ( spell_id ))
2009-05-15 16:46:02 -05:00
newspell . active = ACT_DISABLED ;
2009-05-19 01:23:52 -05:00
else
newspell . active = ACT_PASSIVE ;
2008-11-17 22:07:21 -06:00
}
else
2009-05-15 16:46:02 -05:00
newspell . active = active ;
2009-10-17 15:51:44 -07:00
2009-01-20 19:59:43 -06:00
// talent: unlearn all other talent ranks (high and low)
2009-11-01 17:53:07 -08:00
if ( TalentSpellPos const * talentPos = GetTalentSpellPos ( spell_id ))
2008-11-17 22:07:21 -06:00
{
2009-11-01 17:53:07 -08:00
if ( TalentEntry const * talentInfo = sTalentStore . LookupEntry ( talentPos -> talent_id ))
2009-01-20 19:59:43 -06:00
{
2009-11-01 17:53:07 -08:00
for ( uint8 i = 0 ; i < MAX_TALENT_RANK ; ++ i )
2009-01-20 19:59:43 -06:00
{
// skip learning spell and no rank spell case
uint32 rankSpellId = talentInfo -> RankID [ i ];
2009-11-01 17:53:07 -08:00
if ( ! rankSpellId || rankSpellId == spell_id )
2009-01-20 19:59:43 -06:00
continue ;
2009-10-17 15:51:44 -07:00
2009-01-20 19:59:43 -06:00
// skip unknown ranks
2009-11-01 17:53:07 -08:00
if ( ! HasSpell ( rankSpellId ))
2009-01-20 19:59:43 -06:00
continue ;
2009-05-27 17:02:39 -05:00
removeSpell ( rankSpellId , false , false );
2009-01-20 19:59:43 -06:00
}
}
}
2009-05-17 10:57:55 -05:00
else if ( spellmgr . GetSpellRank ( spell_id ) != 0 )
2009-01-20 19:59:43 -06:00
{
2009-04-29 00:26:07 -05:00
for ( PetSpellMap :: const_iterator itr2 = m_spells . begin (); itr2 != m_spells . end (); ++ itr2 )
2008-11-17 22:07:21 -06:00
{
2009-11-01 17:53:07 -08:00
if ( itr2 -> second . state == PETSPELL_REMOVED ) continue ;
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( spellmgr . IsRankSpellDueToSpell ( spellInfo , itr2 -> first ) )
2009-01-20 19:59:43 -06:00
{
2009-05-17 10:57:55 -05:00
// replace by new high rank
2009-11-01 17:53:07 -08:00
if ( spellmgr . IsHighRankOfSpell ( spell_id , itr2 -> first ))
2009-05-17 10:57:55 -05:00
{
newspell . active = itr2 -> second . active ;
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( newspell . active == ACT_ENABLED )
2009-05-17 10:57:55 -05:00
ToggleAutocast ( itr2 -> first , false );
2009-10-17 15:51:44 -07:00
2009-05-17 10:57:55 -05:00
oldspell_id = itr2 -> first ;
2009-05-27 17:02:39 -05:00
unlearnSpell ( itr2 -> first , false , false );
2009-05-17 10:57:55 -05:00
break ;
}
// ignore new lesser rank
2009-11-01 17:53:07 -08:00
else if ( spellmgr . IsHighRankOfSpell ( itr2 -> first , spell_id ))
2009-05-17 10:57:55 -05:00
return false ;
2009-01-20 19:59:43 -06:00
}
2008-11-17 22:07:21 -06:00
}
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
m_spells [ spell_id ] = newspell ;
2009-10-17 15:51:44 -07:00
2009-10-31 01:50:52 +01:00
if ( IsPassiveSpell ( spell_id ) && ( ! spellInfo -> CasterAuraState || HasAuraState ( AuraState ( spellInfo -> CasterAuraState ))))
2008-11-17 22:07:21 -06:00
CastSpell ( this , spell_id , true );
2009-05-17 10:57:55 -05:00
else
2009-05-27 17:02:39 -05:00
m_charmInfo -> AddSpellToActionBar ( spell_id );
2009-10-17 15:51:44 -07:00
2009-05-15 16:46:02 -05:00
if ( newspell . active == ACT_ENABLED )
2008-11-17 22:07:21 -06:00
ToggleAutocast ( spell_id , true );
2009-10-17 15:51:44 -07:00
2009-01-20 19:59:43 -06:00
uint32 talentCost = GetTalentSpellCost ( spell_id );
if ( talentCost )
{
int32 free_points = GetMaxTalentPointsForLevel ( getLevel ());
2009-11-01 17:53:07 -08:00
m_usedTalentCount += talentCost ;
2009-01-20 19:59:43 -06:00
// update free talent points
free_points -= m_usedTalentCount ;
SetFreeTalentPoints ( free_points > 0 ? free_points : 0 );
}
2008-11-17 22:07:21 -06:00
return true ;
}
2009-10-17 15:51:44 -07:00
2009-01-25 16:35:14 -06:00
bool Pet :: learnSpell ( uint32 spell_id )
2008-11-17 22:07:21 -06:00
{
// prevent duplicated entires in spell book
if ( ! addSpell ( spell_id ))
return false ;
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( ! m_loading )
2008-12-24 09:58:26 -06:00
{
2009-07-01 18:07:20 -05:00
WorldPacket data ( SMSG_PET_LEARNED_SPELL , 4 );
data << uint32 ( spell_id );
2009-04-02 22:03:25 -06:00
m_owner -> GetSession () -> SendPacket ( & data );
m_owner -> PetSpellInitialize ();
2009-01-20 19:59:43 -06:00
}
2008-11-17 22:07:21 -06:00
return true ;
}
2009-10-17 15:51:44 -07:00
2009-05-17 10:57:55 -05:00
void Pet :: InitLevelupSpellsForLevel ()
2008-12-24 09:58:26 -06:00
{
2009-11-01 17:53:07 -08:00
uint8 level = getLevel ();
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( PetLevelupSpellSet const * levelupSpells = GetCreatureInfo () -> family ? spellmgr . GetPetLevelupSpellList ( GetCreatureInfo () -> family ) : NULL )
2009-05-24 12:16:12 -05:00
{
// PetLevelupSpellSet ordered by levels, process in reversed order
2009-10-17 16:20:24 -07:00
for ( PetLevelupSpellSet :: const_reverse_iterator itr = levelupSpells -> rbegin (); itr != levelupSpells -> rend (); ++ itr )
2009-05-24 12:16:12 -05:00
{
// will called first if level down
2009-11-01 17:53:07 -08:00
if ( itr -> first > level )
2009-05-24 12:16:12 -05:00
unlearnSpell ( itr -> second , true ); // will learn prev rank if any
// will called if level up
else
learnSpell ( itr -> second ); // will unlearn prev rank if any
}
}
2009-10-17 15:51:44 -07:00
2009-05-24 12:16:12 -05:00
int32 petSpellsId = GetCreatureInfo () -> PetSpellDataId ? - ( int32 ) GetCreatureInfo () -> PetSpellDataId : GetEntry ();
2009-10-17 15:51:44 -07:00
2009-05-24 12:16:12 -05:00
// default spells (can be not learned if pet level (as owner level decrease result for example) less first possible in normal game)
if ( PetDefaultSpellsEntry const * defSpells = spellmgr . GetPetDefaultSpellsEntry ( petSpellsId ))
2008-12-24 09:58:26 -06:00
{
2009-10-17 16:20:24 -07:00
for ( uint8 i = 0 ; i < MAX_CREATURE_SPELL_DATA_SLOT ; ++ i )
2009-05-24 12:16:12 -05:00
{
SpellEntry const * spellEntry = sSpellStore . LookupEntry ( defSpells -> spellid [ i ]);
2009-11-01 17:53:07 -08:00
if ( ! spellEntry )
2009-05-24 12:16:12 -05:00
continue ;
2009-10-17 15:51:44 -07:00
2009-05-24 12:16:12 -05:00
// will called first if level down
2009-11-01 17:53:07 -08:00
if ( spellEntry -> spellLevel > level )
2009-05-27 17:02:39 -05:00
unlearnSpell ( spellEntry -> Id , true );
2009-05-24 12:16:12 -05:00
// will called if level up
else
learnSpell ( spellEntry -> Id );
}
2008-12-24 09:58:26 -06:00
}
}
2009-10-17 15:51:44 -07:00
2009-05-27 17:02:39 -05:00
bool Pet :: unlearnSpell ( uint32 spell_id , bool learn_prev , bool clear_ab )
2008-12-24 09:58:26 -06:00
{
2009-11-01 17:53:07 -08:00
if ( removeSpell ( spell_id , learn_prev , clear_ab ))
2008-12-24 09:58:26 -06:00
{
2009-11-01 17:53:07 -08:00
if ( ! m_loading )
2008-12-24 09:58:26 -06:00
{
2009-07-01 18:07:20 -05:00
WorldPacket data ( SMSG_PET_REMOVED_SPELL , 4 );
data << uint32 ( spell_id );
2009-04-02 22:03:25 -06:00
m_owner -> GetSession () -> SendPacket ( & data );
2008-12-24 09:58:26 -06:00
}
return true ;
}
return false ;
}
2009-10-17 15:51:44 -07:00
2009-05-27 17:02:39 -05:00
bool Pet :: removeSpell ( uint32 spell_id , bool learn_prev , bool clear_ab )
2008-11-17 22:07:21 -06:00
{
PetSpellMap :: iterator itr = m_spells . find ( spell_id );
if ( itr == m_spells . end ())
2008-12-24 09:58:26 -06:00
return false ;
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( itr -> second . state == PETSPELL_REMOVED )
2008-12-24 09:58:26 -06:00
return false ;
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( itr -> second . state == PETSPELL_NEW )
2008-11-17 22:07:21 -06:00
m_spells . erase ( itr );
else
2009-05-15 16:46:02 -05:00
itr -> second . state = PETSPELL_REMOVED ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
RemoveAurasDueToSpell ( spell_id );
2009-10-17 15:51:44 -07:00
2009-01-20 19:59:43 -06:00
uint32 talentCost = GetTalentSpellCost ( spell_id );
if ( talentCost > 0 )
{
if ( m_usedTalentCount > talentCost )
2009-11-01 17:53:07 -08:00
m_usedTalentCount -= talentCost ;
2009-01-20 19:59:43 -06:00
else
m_usedTalentCount = 0 ;
// update free talent points
int32 free_points = GetMaxTalentPointsForLevel ( getLevel ()) - m_usedTalentCount ;
SetFreeTalentPoints ( free_points > 0 ? free_points : 0 );
}
2009-10-17 15:51:44 -07:00
2009-05-17 10:57:55 -05:00
if ( learn_prev )
{
if ( uint32 prev_id = spellmgr . GetPrevSpellInChain ( spell_id ))
learnSpell ( prev_id );
else
2009-05-24 12:16:12 -05:00
learn_prev = false ;
}
2009-10-17 15:51:44 -07:00
2009-05-24 12:16:12 -05:00
// if remove last rank or non-ranked then update action bar at server and client if need
2009-05-27 17:02:39 -05:00
if ( clear_ab && ! learn_prev && m_charmInfo -> RemoveSpellFromActionBar ( spell_id ))
2009-05-24 12:16:12 -05:00
{
2009-11-01 17:53:07 -08:00
if ( ! m_loading )
2009-05-27 17:02:39 -05:00
{
// need update action bar for last removed rank
if ( Unit * owner = GetOwner ())
if ( owner -> GetTypeId () == TYPEID_PLAYER )
2010-03-07 15:20:19 +01:00
owner -> ToPlayer () -> PetSpellInitialize ();
2009-05-27 17:02:39 -05:00
}
2009-05-17 10:57:55 -05:00
}
2009-10-17 15:51:44 -07:00
2008-12-24 09:58:26 -06:00
return true ;
2008-11-17 22:07:21 -06:00
}
2009-05-27 17:02:39 -05:00
void Pet :: CleanupActionBar ()
{
2009-10-17 16:20:24 -07:00
for ( uint8 i = 0 ; i < MAX_UNIT_ACTION_BAR_INDEX ; ++ i )
2009-11-01 17:53:07 -08:00
if ( UnitActionBarEntry const * ab = m_charmInfo -> GetActionBarEntry ( i ))
if ( ab -> GetAction () && ab -> IsActionBarForSpell ())
2009-05-30 00:05:44 -05:00
{
2009-11-01 17:53:07 -08:00
if ( ! HasSpell ( ab -> GetAction ()))
2009-05-30 00:05:44 -05:00
m_charmInfo -> SetActionBar ( i , 0 , ACT_PASSIVE );
2009-11-01 17:53:07 -08:00
else if ( ab -> GetType () == ACT_ENABLED )
2009-07-01 18:07:20 -05:00
ToggleAutocast ( ab -> GetAction (), true );
2009-05-30 00:05:44 -05:00
}
2009-05-27 17:02:39 -05:00
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
void Pet :: InitPetCreateSpells ()
{
m_charmInfo -> InitPetActionBar ();
m_spells . clear ();
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
LearnPetPassives ();
2009-05-17 10:57:55 -05:00
InitLevelupSpellsForLevel ();
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
CastPetAuras ( false );
}
2009-10-17 15:51:44 -07:00
2009-01-20 19:59:43 -06:00
bool Pet :: resetTalents ( bool no_cost )
{
Unit * owner = GetOwner ();
2009-10-18 18:38:46 -07:00
if ( ! owner || owner -> GetTypeId () != TYPEID_PLAYER )
2009-01-20 19:59:43 -06:00
return false ;
2009-10-17 15:51:44 -07:00
2009-06-23 13:05:15 +02:00
// not need after this call
2010-03-07 15:20:19 +01:00
if ( owner -> ToPlayer () -> HasAtLoginFlag ( AT_LOGIN_RESET_PET_TALENTS ))
owner -> ToPlayer () -> RemoveAtLoginFlag ( AT_LOGIN_RESET_PET_TALENTS , true );
2009-10-17 15:51:44 -07:00
2009-01-20 19:59:43 -06:00
CreatureInfo const * ci = GetCreatureInfo ();
2009-11-01 17:53:07 -08:00
if ( ! ci )
2009-01-20 19:59:43 -06:00
return false ;
// Check pet talent type
CreatureFamilyEntry const * pet_family = sCreatureFamilyStore . LookupEntry ( ci -> family );
2009-11-01 17:53:07 -08:00
if ( ! pet_family || pet_family -> petTalentType < 0 )
2009-01-20 19:59:43 -06:00
return false ;
2009-10-17 15:51:44 -07:00
2009-01-20 19:59:43 -06:00
Player * player = ( Player * ) owner ;
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
uint8 level = getLevel ();
2009-01-20 19:59:43 -06:00
uint32 talentPointsForLevel = GetMaxTalentPointsForLevel ( level );
2009-10-17 15:51:44 -07:00
2009-01-20 19:59:43 -06:00
if ( m_usedTalentCount == 0 )
{
SetFreeTalentPoints ( talentPointsForLevel );
return false ;
}
2009-10-17 15:51:44 -07:00
2009-01-20 19:59:43 -06:00
uint32 cost = 0 ;
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( ! no_cost )
2009-01-20 19:59:43 -06:00
{
cost = resetTalentsCost ();
2009-10-17 15:51:44 -07:00
2009-01-20 19:59:43 -06:00
if ( player -> GetMoney () < cost )
{
2009-11-01 17:53:07 -08:00
player -> SendBuyError ( BUY_ERR_NOT_ENOUGHT_MONEY , 0 , 0 , 0 );
2009-01-20 19:59:43 -06:00
return false ;
}
}
2009-10-17 15:51:44 -07:00
2009-06-18 18:29:28 +02:00
for ( uint32 i = 0 ; i < sTalentStore . GetNumRows (); ++ i )
2009-01-20 19:59:43 -06:00
{
TalentEntry const * talentInfo = sTalentStore . LookupEntry ( i );
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( ! talentInfo )
continue ;
2009-10-17 15:51:44 -07:00
2009-01-20 19:59:43 -06:00
TalentTabEntry const * talentTabInfo = sTalentTabStore . LookupEntry ( talentInfo -> TalentTab );
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( ! talentTabInfo )
2009-01-20 19:59:43 -06:00
continue ;
2009-10-17 15:51:44 -07:00
2009-01-20 19:59:43 -06:00
// unlearn only talents for pets family talent type
2009-11-01 17:53:07 -08:00
if ( ! (( 1 << pet_family -> petTalentType ) & talentTabInfo -> petTalentMask ))
2009-01-20 19:59:43 -06:00
continue ;
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
for ( uint8 j = 0 ; j < MAX_TALENT_RANK ; ++ j )
2009-01-20 19:59:43 -06:00
{
2009-11-20 19:11:28 -08:00
for ( PetSpellMap :: const_iterator itr = m_spells . begin (); itr != m_spells . end ();)
2009-01-20 19:59:43 -06:00
{
2009-11-01 17:53:07 -08:00
if ( itr -> second . state == PETSPELL_REMOVED )
2009-01-20 19:59:43 -06:00
{
++ itr ;
continue ;
}
// remove learned spells (all ranks)
uint32 itrFirstId = spellmgr . GetFirstSpellInChain ( itr -> first );
2009-10-17 15:51:44 -07:00
2009-01-20 19:59:43 -06:00
// unlearn if first rank is talent or learned by talent
if ( itrFirstId == talentInfo -> RankID [ j ] || spellmgr . IsSpellLearnToSpell ( talentInfo -> RankID [ j ], itrFirstId ))
{
2009-05-17 10:57:55 -05:00
removeSpell ( itr -> first , false );
2009-01-20 19:59:43 -06:00
itr = m_spells . begin ();
continue ;
}
else
++ itr ;
}
}
}
2009-10-17 15:51:44 -07:00
2009-01-20 19:59:43 -06:00
SetFreeTalentPoints ( talentPointsForLevel );
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( ! no_cost )
2009-01-20 19:59:43 -06:00
{
player -> ModifyMoney ( - ( int32 ) cost );
2009-10-17 15:51:44 -07:00
2009-01-20 19:59:43 -06:00
m_resetTalentsCost = cost ;
2009-12-24 10:20:15 +01:00
m_resetTalentsTime = time ( NULL );
2009-01-20 19:59:43 -06:00
}
2009-11-01 17:53:07 -08:00
if ( ! m_loading )
2009-03-27 10:13:12 -06:00
player -> PetSpellInitialize ();
2009-01-20 19:59:43 -06:00
return true ;
}
2009-10-17 15:51:44 -07:00
2009-06-23 13:05:15 +02:00
void Pet :: resetTalentsForAllPetsOf ( Player * owner , Pet * online_pet /*= NULL*/ )
{
// not need after this call
2010-03-07 15:20:19 +01:00
if ( owner -> ToPlayer () -> HasAtLoginFlag ( AT_LOGIN_RESET_PET_TALENTS ))
owner -> ToPlayer () -> RemoveAtLoginFlag ( AT_LOGIN_RESET_PET_TALENTS , true );
2009-10-17 15:51:44 -07:00
2009-06-23 13:05:15 +02:00
// reset for online
2009-11-01 17:53:07 -08:00
if ( online_pet )
2009-06-23 13:05:15 +02:00
online_pet -> resetTalents ( true );
2009-10-17 15:51:44 -07:00
2009-06-23 13:05:15 +02:00
// now need only reset for offline pets (all pets except online case)
uint32 except_petnumber = online_pet ? online_pet -> GetCharmInfo () -> GetPetNumber () : 0 ;
2009-10-17 15:51:44 -07:00
2010-01-23 14:45:58 +01:00
QueryResult_AutoPtr resultPets = CharacterDatabase . PQuery (
2009-06-23 13:05:15 +02:00
"SELECT id FROM character_pet WHERE owner = '%u' AND id <> '%u'" ,
owner -> GetGUIDLow (), except_petnumber );
2009-10-17 15:51:44 -07:00
2009-06-23 13:05:15 +02:00
// no offline pets
2009-11-01 17:53:07 -08:00
if ( ! resultPets )
2009-06-23 13:05:15 +02:00
return ;
2009-10-17 15:51:44 -07:00
2010-01-23 14:45:58 +01:00
QueryResult_AutoPtr result = CharacterDatabase . PQuery (
2009-06-23 13:05:15 +02:00
"SELECT DISTINCT pet_spell.spell FROM pet_spell, character_pet "
"WHERE character_pet.owner = '%u' AND character_pet.id = pet_spell.guid AND character_pet.id <> %u" ,
owner -> GetGUIDLow (), except_petnumber );
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( ! result )
2009-06-23 13:05:15 +02:00
return ;
2009-10-17 15:51:44 -07:00
2009-06-23 13:05:15 +02:00
bool need_comma = false ;
std :: ostringstream ss ;
ss << "DELETE FROM pet_spell WHERE guid IN (" ;
2009-10-17 15:51:44 -07:00
2009-06-23 13:05:15 +02:00
do
{
Field * fields = resultPets -> Fetch ();
2009-10-17 15:51:44 -07:00
2009-06-23 13:05:15 +02:00
uint32 id = fields [ 0 ]. GetUInt32 ();
2009-10-17 15:51:44 -07:00
2009-06-23 13:05:15 +02:00
if ( need_comma )
ss << "," ;
2009-10-17 15:51:44 -07:00
2009-06-23 13:05:15 +02:00
ss << id ;
2009-10-17 15:51:44 -07:00
2009-06-23 13:05:15 +02:00
need_comma = true ;
}
2009-11-01 17:53:07 -08:00
while ( resultPets -> NextRow ());
2009-10-17 15:51:44 -07:00
2009-06-23 13:05:15 +02:00
ss << ") AND spell IN (" ;
2009-10-17 15:51:44 -07:00
2009-06-23 13:05:15 +02:00
bool need_execute = false ;
do
{
Field * fields = result -> Fetch ();
2009-10-17 15:51:44 -07:00
2009-06-23 13:05:15 +02:00
uint32 spell = fields [ 0 ]. GetUInt32 ();
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( ! GetTalentSpellCost ( spell ))
2009-06-23 13:05:15 +02:00
continue ;
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( need_execute )
2009-06-23 13:05:15 +02:00
ss << "," ;
2009-10-17 15:51:44 -07:00
2009-06-23 13:05:15 +02:00
ss << spell ;
2009-10-17 15:51:44 -07:00
2009-06-23 13:05:15 +02:00
need_execute = true ;
}
2009-11-01 17:53:07 -08:00
while ( result -> NextRow ());
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( ! need_execute )
2009-06-23 13:05:15 +02:00
return ;
2009-10-17 15:51:44 -07:00
2009-06-23 13:05:15 +02:00
ss << ")" ;
2009-10-17 15:51:44 -07:00
2009-06-23 13:05:15 +02:00
CharacterDatabase . Execute ( ss . str (). c_str ());
}
2009-10-17 15:51:44 -07:00
2009-01-20 19:59:43 -06:00
void Pet :: InitTalentForLevel ()
{
2009-11-01 17:53:07 -08:00
uint8 level = getLevel ();
2009-01-20 19:59:43 -06:00
uint32 talentPointsForLevel = GetMaxTalentPointsForLevel ( level );
// Reset talents in case low level (on level down) or wrong points for level (hunter can unlearn TP increase talent)
if ( talentPointsForLevel == 0 || m_usedTalentCount > talentPointsForLevel )
2009-11-01 17:53:07 -08:00
resetTalents ( true ); // Remove all talent points
2009-01-20 19:59:43 -06:00
SetFreeTalentPoints ( talentPointsForLevel - m_usedTalentCount );
2009-10-17 15:51:44 -07:00
2009-06-11 00:45:59 -05:00
Unit * owner = GetOwner ();
if ( ! owner || owner -> GetTypeId () != TYPEID_PLAYER )
return ;
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( ! m_loading )
2010-03-07 15:20:19 +01:00
owner -> ToPlayer () -> SendTalentsInfoData ( true );
2009-01-20 19:59:43 -06:00
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
uint32 Pet :: resetTalentsCost () const
{
2009-12-24 10:20:15 +01:00
uint32 days = ( sWorld . GetGameTime () - m_resetTalentsTime ) / DAY ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
// The first time reset costs 10 silver; after 1 day cost is reset to 10 silver
2009-11-01 17:53:07 -08:00
if ( m_resetTalentsCost < 10 * SILVER || days > 0 )
2008-11-17 22:07:21 -06:00
return 10 * SILVER ;
// then 50 silver
2009-11-01 17:53:07 -08:00
else if ( m_resetTalentsCost < 50 * SILVER )
2008-11-17 22:07:21 -06:00
return 50 * SILVER ;
// then 1 gold
2009-11-01 17:53:07 -08:00
else if ( m_resetTalentsCost < 1 * GOLD )
2008-11-17 22:07:21 -06:00
return 1 * GOLD ;
// then increasing at a rate of 1 gold; cap 10 gold
else
return ( m_resetTalentsCost + 1 * GOLD > 10 * GOLD ? 10 * GOLD : m_resetTalentsCost + 1 * GOLD );
}
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
uint8 Pet :: GetMaxTalentPointsForLevel ( uint8 level )
2009-01-20 19:59:43 -06:00
{
uint8 points = ( level >= 20 ) ? (( level - 16 ) / 4 ) : 0 ;
// Mod points from owner SPELL_AURA_MOD_PET_TALENT_POINTS
if ( Unit * owner = GetOwner ())
points += owner -> GetTotalAuraModifier ( SPELL_AURA_MOD_PET_TALENT_POINTS );
return points ;
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
void Pet :: ToggleAutocast ( uint32 spellid , bool apply )
{
2009-11-01 17:53:07 -08:00
if ( ! IsAutocastableSpell ( spellid ))
2008-11-17 22:07:21 -06:00
return ;
2009-10-17 15:51:44 -07:00
2009-05-15 16:46:02 -05:00
PetSpellMap :: iterator itr = m_spells . find ( spellid );
2009-11-01 17:53:07 -08:00
if ( itr == m_spells . end ())
2009-03-29 09:45:47 -06:00
return ;
2009-10-17 15:51:44 -07:00
2009-06-18 18:29:28 +02:00
uint32 i ;
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( apply )
2008-11-17 22:07:21 -06:00
{
2009-04-29 00:26:07 -05:00
for ( i = 0 ; i < m_autospells . size () && m_autospells [ i ] != spellid ; ++ i )
2008-12-24 09:58:26 -06:00
; // just search
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
if ( i == m_autospells . size ())
{
m_autospells . push_back ( spellid );
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( itr -> second . active != ACT_ENABLED )
2009-05-03 22:27:39 -05:00
{
2009-05-15 16:46:02 -05:00
itr -> second . active = ACT_ENABLED ;
2009-11-01 17:53:07 -08:00
if ( itr -> second . state != PETSPELL_NEW )
2009-05-15 16:46:02 -05:00
itr -> second . state = PETSPELL_CHANGED ;
2009-05-03 22:27:39 -05:00
}
2008-11-17 22:07:21 -06:00
}
}
else
{
AutoSpellList :: iterator itr2 = m_autospells . begin ();
2009-11-01 17:53:07 -08:00
for ( i = 0 ; i < m_autospells . size () && m_autospells [ i ] != spellid ; ++ i , ++ itr2 )
2008-12-24 09:58:26 -06:00
; // just search
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
if ( i < m_autospells . size ())
{
m_autospells . erase ( itr2 );
2009-05-15 16:46:02 -05:00
if ( itr -> second . active != ACT_DISABLED )
2009-05-03 22:27:39 -05:00
{
2009-05-15 16:46:02 -05:00
itr -> second . active = ACT_DISABLED ;
if ( itr -> second . state != PETSPELL_NEW )
itr -> second . state = PETSPELL_CHANGED ;
2009-05-03 22:27:39 -05:00
}
2008-11-17 22:07:21 -06:00
}
}
}
2009-10-17 15:51:44 -07:00
2009-05-08 18:43:06 -05:00
bool Pet :: IsPermanentPetFor ( Player * owner )
{
2009-11-01 17:53:07 -08:00
switch ( getPetType ())
2009-05-08 18:43:06 -05:00
{
case SUMMON_PET :
2009-11-01 17:53:07 -08:00
switch ( owner -> getClass ())
2009-05-08 18:43:06 -05:00
{
case CLASS_WARLOCK :
return GetCreatureInfo () -> type == CREATURE_TYPE_DEMON ;
case CLASS_DEATH_KNIGHT :
return GetCreatureInfo () -> type == CREATURE_TYPE_UNDEAD ;
default :
return false ;
}
case HUNTER_PET :
return true ;
default :
return false ;
}
}
2009-10-17 15:51:44 -07:00
2009-01-31 16:38:50 -06:00
bool Pet :: Create ( uint32 guidlow , Map * map , uint32 phaseMask , uint32 Entry , uint32 pet_number )
2008-11-17 22:07:21 -06:00
{
2009-08-01 00:20:32 +02:00
assert ( map );
2009-07-16 11:49:00 +08:00
SetMap ( map );
2009-10-17 15:51:44 -07:00
2009-08-01 00:20:32 +02:00
SetPhaseMask ( phaseMask , false );
2008-11-17 22:07:21 -06:00
Object :: _Create ( guidlow , pet_number , HIGHGUID_PET );
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
m_DBTableGuid = guidlow ;
m_originalEntry = Entry ;
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( ! InitEntry ( Entry ))
2008-11-17 22:07:21 -06:00
return false ;
2009-10-17 15:51:44 -07:00
2009-05-28 18:43:57 -05:00
SetSheath ( SHEATH_STATE_MELEE );
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
return true ;
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
bool Pet :: HasSpell ( uint32 spell ) const
{
2009-01-25 16:35:14 -06:00
PetSpellMap :: const_iterator itr = m_spells . find ( spell );
2009-11-01 17:53:07 -08:00
return itr != m_spells . end () && itr -> second . state != PETSPELL_REMOVED ;
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
// Get all passive spells in our skill line
void Pet :: LearnPetPassives ()
{
CreatureInfo const * cInfo = GetCreatureInfo ();
2009-11-01 17:53:07 -08:00
if ( ! cInfo )
2008-11-17 22:07:21 -06:00
return ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
CreatureFamilyEntry const * cFamily = sCreatureFamilyStore . LookupEntry ( cInfo -> family );
2009-11-01 17:53:07 -08:00
if ( ! cFamily )
2008-11-17 22:07:21 -06:00
return ;
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
PetFamilySpellsStore :: const_iterator petStore = sPetFamilySpellsStore . find ( cFamily -> ID );
2009-11-01 17:53:07 -08:00
if ( petStore != sPetFamilySpellsStore . end ())
2008-11-17 22:07:21 -06:00
{
2008-12-20 12:30:17 -06:00
// For general hunter pets skill 270
// Passive 01~10, Passive 00 (20782, not used), Ferocious Inspiration (34457)
// Scale 01~03 (34902~34904, bonus from owner, not used)
2009-10-17 16:20:24 -07:00
for ( PetFamilySpellsSet :: const_iterator petSet = petStore -> second . begin (); petSet != petStore -> second . end (); ++ petSet )
2009-01-09 16:06:17 -06:00
addSpell ( * petSet , ACT_DECIDE , PETSPELL_NEW , PETSPELL_FAMILY );
2008-11-17 22:07:21 -06:00
}
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
void Pet :: CastPetAuras ( bool current )
{
Unit * owner = GetOwner ();
2009-11-01 17:53:07 -08:00
if ( ! owner || owner -> GetTypeId () != TYPEID_PLAYER )
2008-11-17 22:07:21 -06:00
return ;
2009-10-17 15:51:44 -07:00
2010-03-07 15:20:19 +01:00
if ( ! IsPermanentPetFor ( owner -> ToPlayer ()))
2008-11-17 22:07:21 -06:00
return ;
2009-10-17 15:51:44 -07:00
2009-11-20 19:11:28 -08:00
for ( PetAuraSet :: const_iterator itr = owner -> m_petAuras . begin (); itr != owner -> m_petAuras . end ();)
2008-11-17 22:07:21 -06:00
{
PetAura const * pa = * itr ;
++ itr ;
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( ! current && pa -> IsRemovedOnChangePet ())
2008-11-17 22:07:21 -06:00
owner -> RemovePetAura ( pa );
else
CastPetAura ( pa );
}
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
void Pet :: CastPetAura ( PetAura const * aura )
{
2009-07-01 18:07:20 -05:00
uint32 auraId = aura -> GetAura ( GetEntry ());
2009-11-01 17:53:07 -08:00
if ( ! auraId )
2008-11-17 22:07:21 -06:00
return ;
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( auraId == 35696 ) // Demonic Knowledge
2008-11-17 22:07:21 -06:00
{
int32 basePoints = int32 ( aura -> GetDamage () * ( GetStat ( STAT_STAMINA ) + GetStat ( STAT_INTELLECT )) / 100 );
2008-12-12 11:21:28 -06:00
CastCustomSpell ( this , auraId , & basePoints , NULL , NULL , true );
2008-11-17 22:07:21 -06:00
}
else
CastSpell ( this , auraId , true );
}
2009-10-17 15:51:44 -07:00
2009-03-28 16:25:42 -06:00
void Pet :: learnSpellHighRank ( uint32 spellid )
{
2009-03-28 17:40:07 -06:00
learnSpell ( spellid );
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( uint32 next = spellmgr . GetNextSpellInChain ( spellid ))
2009-07-31 13:21:01 +02:00
learnSpellHighRank ( next );
2009-03-28 16:25:42 -06:00
}
2009-10-17 15:51:44 -07:00
2009-05-15 13:44:49 -05:00
void Pet :: SynchronizeLevelWithOwner ()
{
Unit * owner = GetOwner ();
if ( ! owner || owner -> GetTypeId () != TYPEID_PLAYER )
return ;
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
switch ( getPetType ())
2009-05-15 13:44:49 -05:00
{
// always same level
case SUMMON_PET :
GivePetLevel ( owner -> getLevel ());
break ;
// can't be greater owner level
case HUNTER_PET :
2009-11-01 17:53:07 -08:00
if ( getLevel () > owner -> getLevel ())
2009-05-15 13:44:49 -05:00
{
GivePetLevel ( owner -> getLevel ());
2010-02-14 19:13:14 -07:00
SetUInt32Value ( UNIT_FIELD_PETNEXTLEVELEXP , objmgr . GetXPForLevel ( owner -> getLevel ()) / 5 );
2009-05-15 13:44:49 -05:00
SetUInt32Value ( UNIT_FIELD_PETEXPERIENCE , GetUInt32Value ( UNIT_FIELD_PETNEXTLEVELEXP ) - 1 );
}
break ;
default :
break ;
}
}