2 * Copyright (C) 2005-2010 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 "WorldPacket.h"
21 #include "WorldSession.h"
22 #include "ObjectMgr.h"
27 #include "ObjectAccessor.h"
28 #include "CreatureAI.h"
32 void WorldSession::HandlePetAction( WorldPacket
& recv_data
)
37 recv_data
>> guid1
; //pet guid
39 recv_data
>> guid2
; //tag guid
41 uint32 spellid
= UNIT_ACTION_BUTTON_ACTION(data
);
42 uint8 flag
= UNIT_ACTION_BUTTON_TYPE(data
); //delete = 0x07 CastSpell = C1
44 // used also for charmed creature
45 Unit
* pet
= ObjectAccessor::GetUnit(*_player
, guid1
);
46 DETAIL_LOG("HandlePetAction.Pet %u flag is %u, spellid is %u, target %u.", uint32(GUID_LOPART(guid1
)), uint32(flag
), spellid
, uint32(GUID_LOPART(guid2
)) );
49 sLog
.outError( "Pet %u not exist.", uint32(GUID_LOPART(guid1
)) );
53 if (pet
!= GetPlayer()->GetPet() && pet
!= GetPlayer()->GetCharm())
55 sLog
.outError("HandlePetAction.Pet %u isn't pet of player %s.", uint32(GUID_LOPART(guid1
)), GetPlayer()->GetName() );
62 if (pet
->GetTypeId() == TYPEID_PLAYER
)
64 // controller player can only do melee attack
65 if (!(flag
== ACT_COMMAND
&& spellid
== COMMAND_ATTACK
))
68 else if (((Creature
*)pet
)->isPet())
70 // pet can have action bar disabled
71 if(((Pet
*)pet
)->GetModeFlags() & PET_MODE_DISABLE_ACTIONS
)
75 CharmInfo
*charmInfo
= pet
->GetCharmInfo();
78 sLog
.outError("WorldSession::HandlePetAction: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet
->GetGUIDLow(), pet
->GetTypeId());
84 case ACT_COMMAND
: //0x07
87 case COMMAND_STAY
: //flat=1792 //STAY
89 pet
->GetMotionMaster()->Clear();
90 pet
->GetMotionMaster()->MoveIdle();
91 charmInfo
->SetCommandState( COMMAND_STAY
);
93 case COMMAND_FOLLOW
: //spellid=1792 //FOLLOW
95 pet
->GetMotionMaster()->MoveFollow(_player
,PET_FOLLOW_DIST
,PET_FOLLOW_ANGLE
);
96 charmInfo
->SetCommandState( COMMAND_FOLLOW
);
98 case COMMAND_ATTACK
: //spellid=1792 //ATTACK
100 const uint64
& selguid
= _player
->GetSelection();
101 Unit
*TargetUnit
= ObjectAccessor::GetUnit(*_player
, selguid
);
105 // not let attack friendly units.
106 if(GetPlayer()->IsFriendlyTo(TargetUnit
))
108 // Not let attack through obstructions
109 if(!pet
->IsWithinLOSInMap(TargetUnit
))
112 // This is true if pet has no target or has target but targets differs.
113 if(pet
->getVictim() != TargetUnit
)
115 if (pet
->getVictim())
118 if(pet
->GetTypeId() != TYPEID_PLAYER
)
120 pet
->GetMotionMaster()->Clear();
121 if (((Creature
*)pet
)->AI())
122 ((Creature
*)pet
)->AI()->AttackStart(TargetUnit
);
124 //10% chance to play special pet attack talk, else growl
125 if(((Creature
*)pet
)->isPet() && ((Pet
*)pet
)->getPetType() == SUMMON_PET
&& pet
!= TargetUnit
&& urand(0, 100) < 10)
126 pet
->SendPetTalk((uint32
)PET_TALK_ATTACK
);
129 // 90% chance for pet and 100% chance for charmed creature
130 pet
->SendPetAIReaction(guid1
);
133 else // charmed player
135 pet
->Attack(TargetUnit
,true);
136 pet
->SendPetAIReaction(guid1
);
141 case COMMAND_ABANDON
: // abandon (hunter pet) or dismiss (summoned pet)
142 if(((Creature
*)pet
)->isPet())
145 if(p
->getPetType() == HUNTER_PET
)
146 _player
->RemovePet(p
,PET_SAVE_AS_DELETED
);
148 //dismissing a summoned pet is like killing them (this prevents returning a soulshard...)
149 p
->setDeathState(CORPSE
);
155 sLog
.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag
), spellid
);
158 case ACT_REACTION
: // 0x6
161 case REACT_PASSIVE
: //passive
162 case REACT_DEFENSIVE
: //recovery
163 case REACT_AGGRESSIVE
: //activete
164 charmInfo
->SetReactState( ReactStates(spellid
) );
168 case ACT_DISABLED
: // 0x81 spell (disabled), ignore
169 case ACT_PASSIVE
: // 0x01
170 case ACT_ENABLED
: // 0xC1 spell
172 Unit
* unit_target
= NULL
;
173 if (((Creature
*)pet
)->GetGlobalCooldown() > 0)
177 unit_target
= ObjectAccessor::GetUnit(*_player
,guid2
);
179 // do not cast unknown spells
180 SpellEntry
const *spellInfo
= sSpellStore
.LookupEntry(spellid
);
183 sLog
.outError("WORLD: unknown PET spell id %i", spellid
);
187 for(int i
= 0; i
< MAX_EFFECT_INDEX
;++i
)
189 if(spellInfo
->EffectImplicitTargetA
[i
] == TARGET_ALL_ENEMY_IN_AREA
|| spellInfo
->EffectImplicitTargetA
[i
] == TARGET_ALL_ENEMY_IN_AREA_INSTANT
|| spellInfo
->EffectImplicitTargetA
[i
] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED
)
193 // do not cast not learned spells
194 if(!pet
->HasSpell(spellid
) || IsPassiveSpell(spellid
))
197 pet
->clearUnitState(UNIT_STAT_MOVING
);
199 Spell
*spell
= new Spell(pet
, spellInfo
, false);
201 SpellCastResult result
= spell
->CheckPetCast(unit_target
);
203 //auto turn to target unless possessed
204 if(result
== SPELL_FAILED_UNIT_NOT_INFRONT
&& !pet
->HasAuraType(SPELL_AURA_MOD_POSSESS
))
208 pet
->SetInFront(unit_target
);
209 if (unit_target
->GetTypeId() == TYPEID_PLAYER
)
210 pet
->SendCreateUpdateToPlayer( (Player
*)unit_target
);
212 else if(Unit
*unit_target2
= spell
->m_targets
.getUnitTarget())
214 pet
->SetInFront(unit_target2
);
215 if (unit_target2
->GetTypeId() == TYPEID_PLAYER
)
216 pet
->SendCreateUpdateToPlayer( (Player
*)unit_target2
);
218 if (Unit
* powner
= pet
->GetCharmerOrOwner())
219 if(powner
->GetTypeId() == TYPEID_PLAYER
)
220 pet
->SendCreateUpdateToPlayer((Player
*)powner
);
221 result
= SPELL_CAST_OK
;
224 if(result
== SPELL_CAST_OK
)
226 ((Creature
*)pet
)->AddCreatureSpellCooldown(spellid
);
228 unit_target
= spell
->m_targets
.getUnitTarget();
230 //10% chance to play special pet attack talk, else growl
231 //actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
232 if(((Creature
*)pet
)->isPet() && (((Pet
*)pet
)->getPetType() == SUMMON_PET
) && (pet
!= unit_target
) && (urand(0, 100) < 10))
233 pet
->SendPetTalk((uint32
)PET_TALK_SPECIAL_SPELL
);
236 pet
->SendPetAIReaction(guid1
);
239 if( unit_target
&& !GetPlayer()->IsFriendlyTo(unit_target
) && !pet
->HasAuraType(SPELL_AURA_MOD_POSSESS
))
241 // This is true if pet has no target or has target but targets differs.
242 if (pet
->getVictim() != unit_target
)
244 if (pet
->getVictim())
246 pet
->GetMotionMaster()->Clear();
247 if (((Creature
*)pet
)->AI())
248 ((Creature
*)pet
)->AI()->AttackStart(unit_target
);
252 spell
->prepare(&(spell
->m_targets
));
256 if(pet
->HasAuraType(SPELL_AURA_MOD_POSSESS
))
257 Spell::SendCastResult(GetPlayer(),spellInfo
,0,result
);
259 pet
->SendPetCastFail(spellid
, result
);
261 if (!((Creature
*)pet
)->HasSpellCooldown(spellid
))
262 GetPlayer()->SendClearCooldown(spellid
, pet
);
264 spell
->finish(false);
270 sLog
.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag
), spellid
);
274 void WorldSession::HandlePetNameQuery( WorldPacket
& recv_data
)
276 DETAIL_LOG( "HandlePetNameQuery. CMSG_PET_NAME_QUERY" );
281 recv_data
>> petnumber
;
282 recv_data
>> petguid
;
284 SendPetNameQuery(petguid
,petnumber
);
287 void WorldSession::SendPetNameQuery( uint64 petguid
, uint32 petnumber
)
289 Creature
* pet
= _player
->GetMap()->GetCreatureOrPetOrVehicle(petguid
);
290 if(!pet
|| !pet
->GetCharmInfo() || pet
->GetCharmInfo()->GetPetNumber() != petnumber
)
293 std::string name
= pet
->GetName();
295 WorldPacket
data(SMSG_PET_NAME_QUERY_RESPONSE
, (4+4+name
.size()+1));
296 data
<< uint32(petnumber
);
297 data
<< name
.c_str();
298 data
<< uint32(pet
->GetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP
));
300 if( pet
->isPet() && ((Pet
*)pet
)->GetDeclinedNames() )
303 for(int i
= 0; i
< MAX_DECLINED_NAME_CASES
; ++i
)
304 data
<< ((Pet
*)pet
)->GetDeclinedNames()->name
[i
];
309 _player
->GetSession()->SendPacket(&data
);
312 void WorldSession::HandlePetSetAction( WorldPacket
& recv_data
)
314 DETAIL_LOG( "HandlePetSetAction. CMSG_PET_SET_ACTION" );
319 recv_data
>> petguid
;
321 Creature
* pet
= _player
->GetMap()->GetCreatureOrPetOrVehicle(petguid
);
323 if(!pet
|| (pet
!= _player
->GetPet() && pet
!= _player
->GetCharm()))
325 sLog
.outError( "HandlePetSetAction: Unknown pet or pet owner." );
329 // pet can have action bar disabled
330 if(pet
->isPet() && ((Pet
*)pet
)->GetModeFlags() & PET_MODE_DISABLE_ACTIONS
)
333 CharmInfo
*charmInfo
= pet
->GetCharmInfo();
336 sLog
.outError("WorldSession::HandlePetSetAction: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet
->GetGUIDLow(), pet
->GetTypeId());
340 count
= (recv_data
.size() == 24) ? 2 : 1;
344 bool move_command
= false;
346 for(uint8 i
= 0; i
< count
; ++i
)
348 recv_data
>> position
[i
];
349 recv_data
>> data
[i
];
351 uint8 act_state
= UNIT_ACTION_BUTTON_TYPE(data
[i
]);
353 //ignore invalid position
354 if(position
[i
] >= MAX_UNIT_ACTION_BAR_INDEX
)
357 // in the normal case, command and reaction buttons can only be moved, not removed
358 // at moving count ==2, at removing count == 1
359 // ignore attempt to remove command|reaction buttons (not possible at normal case)
360 if (act_state
== ACT_COMMAND
|| act_state
== ACT_REACTION
)
369 // check swap (at command->spell swap client remove spell first in another packet, so check only command move correctness)
372 uint8 act_state_0
= UNIT_ACTION_BUTTON_TYPE(data
[0]);
373 if(act_state_0
== ACT_COMMAND
|| act_state_0
== ACT_REACTION
)
375 uint32 spell_id_0
= UNIT_ACTION_BUTTON_ACTION(data
[0]);
376 UnitActionBarEntry
const* actionEntry_1
= charmInfo
->GetActionBarEntry(position
[1]);
377 if (!actionEntry_1
|| spell_id_0
!= actionEntry_1
->GetAction() ||
378 act_state_0
!= actionEntry_1
->GetType())
382 uint8 act_state_1
= UNIT_ACTION_BUTTON_TYPE(data
[1]);
383 if(act_state_1
== ACT_COMMAND
|| act_state_1
== ACT_REACTION
)
385 uint32 spell_id_1
= UNIT_ACTION_BUTTON_ACTION(data
[1]);
386 UnitActionBarEntry
const* actionEntry_0
= charmInfo
->GetActionBarEntry(position
[0]);
387 if (!actionEntry_0
|| spell_id_1
!= actionEntry_0
->GetAction() ||
388 act_state_1
!= actionEntry_0
->GetType())
393 for(uint8 i
= 0; i
< count
; ++i
)
395 uint32 spell_id
= UNIT_ACTION_BUTTON_ACTION(data
[i
]);
396 uint8 act_state
= UNIT_ACTION_BUTTON_TYPE(data
[i
]);
398 DETAIL_LOG( "Player %s has changed pet spell action. Position: %u, Spell: %u, State: 0x%X", _player
->GetName(), position
[i
], spell_id
, uint32(act_state
));
400 //if it's act for spell (en/disable/cast) and there is a spell given (0 = remove spell) which pet doesn't know, don't add
401 if(!((act_state
== ACT_ENABLED
|| act_state
== ACT_DISABLED
|| act_state
== ACT_PASSIVE
) && spell_id
&& !pet
->HasSpell(spell_id
)))
404 if(act_state
== ACT_ENABLED
&& spell_id
)
407 charmInfo
->ToggleCreatureAutocast(spell_id
, true);
409 ((Pet
*)pet
)->ToggleAutocast(spell_id
, true);
411 //sign for no/turn off autocast
412 else if(act_state
== ACT_DISABLED
&& spell_id
)
415 charmInfo
->ToggleCreatureAutocast(spell_id
, false);
417 ((Pet
*)pet
)->ToggleAutocast(spell_id
, false);
420 charmInfo
->SetActionBar(position
[i
],spell_id
,ActiveStates(act_state
));
425 void WorldSession::HandlePetRename( WorldPacket
& recv_data
)
427 DETAIL_LOG( "HandlePetRename. CMSG_PET_RENAME" );
433 DeclinedName declinedname
;
435 recv_data
>> petguid
;
437 recv_data
>> isdeclined
;
439 Pet
* pet
= _player
->GetMap()->GetPet(petguid
);
441 if( !pet
|| pet
->getPetType() != HUNTER_PET
||
442 !pet
->HasByteFlag(UNIT_FIELD_BYTES_2
, 2, UNIT_CAN_BE_RENAMED
) ||
443 pet
->GetOwnerGUID() != _player
->GetGUID() || !pet
->GetCharmInfo() )
446 PetNameInvalidReason res
= ObjectMgr::CheckPetName(name
);
447 if(res
!= PET_NAME_SUCCESS
)
449 SendPetNameInvalid(res
, name
, NULL
);
453 if(sObjectMgr
.IsReservedName(name
))
455 SendPetNameInvalid(PET_NAME_RESERVED
, name
, NULL
);
461 if(_player
->GetGroup())
462 _player
->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_NAME
);
464 pet
->RemoveByteFlag(UNIT_FIELD_BYTES_2
, 2, UNIT_CAN_BE_RENAMED
);
468 for(int i
= 0; i
< MAX_DECLINED_NAME_CASES
; ++i
)
470 recv_data
>> declinedname
.name
[i
];
474 Utf8toWStr(name
, wname
);
475 if(!ObjectMgr::CheckDeclinedNames(GetMainPartOfName(wname
,0),declinedname
))
477 SendPetNameInvalid(PET_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME
, name
, &declinedname
);
482 CharacterDatabase
.BeginTransaction();
485 for(int i
= 0; i
< MAX_DECLINED_NAME_CASES
; ++i
)
486 CharacterDatabase
.escape_string(declinedname
.name
[i
]);
487 CharacterDatabase
.PExecute("DELETE FROM character_pet_declinedname WHERE owner = '%u' AND id = '%u'", _player
->GetGUIDLow(), pet
->GetCharmInfo()->GetPetNumber());
488 CharacterDatabase
.PExecute("INSERT INTO character_pet_declinedname (id, owner, genitive, dative, accusative, instrumental, prepositional) VALUES ('%u','%u','%s','%s','%s','%s','%s')",
489 pet
->GetCharmInfo()->GetPetNumber(), _player
->GetGUIDLow(), declinedname
.name
[0].c_str(), declinedname
.name
[1].c_str(), declinedname
.name
[2].c_str(), declinedname
.name
[3].c_str(), declinedname
.name
[4].c_str());
492 CharacterDatabase
.escape_string(name
);
493 CharacterDatabase
.PExecute("UPDATE character_pet SET name = '%s', renamed = '1' WHERE owner = '%u' AND id = '%u'", name
.c_str(), _player
->GetGUIDLow(), pet
->GetCharmInfo()->GetPetNumber());
494 CharacterDatabase
.CommitTransaction();
496 pet
->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP
, uint32(time(NULL
)));
499 void WorldSession::HandlePetAbandon( WorldPacket
& recv_data
)
502 recv_data
>> guid
; //pet guid
503 DETAIL_LOG( "HandlePetAbandon. CMSG_PET_ABANDON pet guid is %u", GUID_LOPART(guid
) );
505 if(!_player
->IsInWorld())
509 if (Creature
* pet
= _player
->GetMap()->GetCreatureOrPetOrVehicle(guid
))
513 if(pet
->GetGUID() == _player
->GetPetGUID())
515 uint32 feelty
= pet
->GetPower(POWER_HAPPINESS
);
516 pet
->SetPower(POWER_HAPPINESS
,(feelty
-50000) > 0 ?(feelty
-50000) : 0);
519 _player
->RemovePet((Pet
*)pet
,PET_SAVE_AS_DELETED
);
521 else if(pet
->GetGUID() == _player
->GetCharmGUID())
528 void WorldSession::HandlePetUnlearnOpcode(WorldPacket
& recvPacket
)
530 DETAIL_LOG("CMSG_PET_UNLEARN");
532 recvPacket
>> guid
; // Pet guid
534 Pet
* pet
= _player
->GetPet();
536 if(!pet
|| pet
->getPetType() != HUNTER_PET
|| pet
->m_usedTalentCount
== 0)
539 if(guid
!= pet
->GetGUID())
541 sLog
.outError( "HandlePetUnlearnOpcode.Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid
)),GetPlayer()->GetName() );
545 CharmInfo
*charmInfo
= pet
->GetCharmInfo();
548 sLog
.outError("WorldSession::HandlePetUnlearnOpcode: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet
->GetGUIDLow(), pet
->GetTypeId());
552 _player
->SendTalentsInfoData(true);
555 void WorldSession::HandlePetSpellAutocastOpcode( WorldPacket
& recvPacket
)
557 DETAIL_LOG("CMSG_PET_SPELL_AUTOCAST");
560 uint8 state
; //1 for on, 0 for off
561 recvPacket
>> guid
>> spellid
>> state
;
563 if(!_player
->GetPet() && !_player
->GetCharm())
566 Creature
* pet
= _player
->GetMap()->GetCreatureOrPetOrVehicle(guid
);
568 if(!pet
|| (pet
!= _player
->GetPet() && pet
!= _player
->GetCharm()))
570 sLog
.outError( "HandlePetSpellAutocastOpcode.Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid
)),GetPlayer()->GetName() );
574 // do not add not learned spells/ passive spells
575 if(!pet
->HasSpell(spellid
) || IsPassiveSpell(spellid
))
578 CharmInfo
*charmInfo
= pet
->GetCharmInfo();
581 sLog
.outError("WorldSession::HandlePetSpellAutocastOpcod: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet
->GetGUIDLow(), pet
->GetTypeId());
586 //state can be used as boolean
587 pet
->GetCharmInfo()->ToggleCreatureAutocast(spellid
, state
);
589 ((Pet
*)pet
)->ToggleAutocast(spellid
, state
);
591 charmInfo
->SetSpellAutocast(spellid
,state
);
594 void WorldSession::HandlePetCastSpellOpcode( WorldPacket
& recvPacket
)
596 DETAIL_LOG("WORLD: CMSG_PET_CAST_SPELL");
601 uint8 unk_flags
; // flags (if 0x02 - some additional data are received)
603 recvPacket
>> guid
>> cast_count
>> spellid
>> unk_flags
;
605 DEBUG_LOG("WORLD: CMSG_PET_CAST_SPELL, cast_count: %u, spellid %u, unk_flags %u", cast_count
, spellid
, unk_flags
);
607 if (!_player
->GetPet() && !_player
->GetCharm())
610 Creature
* pet
= _player
->GetMap()->GetCreatureOrPetOrVehicle(guid
);
612 if (!pet
|| (pet
!= _player
->GetPet() && pet
!= _player
->GetCharm()))
614 sLog
.outError( "HandlePetCastSpellOpcode: Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid
)),GetPlayer()->GetName() );
618 if (pet
->GetGlobalCooldown() > 0)
621 SpellEntry
const *spellInfo
= sSpellStore
.LookupEntry(spellid
);
624 sLog
.outError("WORLD: unknown PET spell id %i", spellid
);
628 // do not cast not learned spells
629 if (!pet
->HasSpell(spellid
) || IsPassiveSpell(spellid
))
632 SpellCastTargets targets
;
634 recvPacket
>> targets
.ReadForCaster(pet
);
636 pet
->clearUnitState(UNIT_STAT_MOVING
);
638 Spell
*spell
= new Spell(pet
, spellInfo
, false);
639 spell
->m_cast_count
= cast_count
; // probably pending spell cast
640 spell
->m_targets
= targets
;
642 SpellCastResult result
= spell
->CheckPetCast(NULL
);
643 if (result
== SPELL_CAST_OK
)
645 pet
->AddCreatureSpellCooldown(spellid
);
648 //10% chance to play special pet attack talk, else growl
649 //actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
650 if(((Pet
*)pet
)->getPetType() == SUMMON_PET
&& (urand(0, 100) < 10))
651 pet
->SendPetTalk((uint32
)PET_TALK_SPECIAL_SPELL
);
653 pet
->SendPetAIReaction(guid
);
656 spell
->prepare(&(spell
->m_targets
));
660 pet
->SendPetCastFail(spellid
, result
);
661 if (!pet
->HasSpellCooldown(spellid
))
662 GetPlayer()->SendClearCooldown(spellid
, pet
);
664 spell
->finish(false);
669 void WorldSession::SendPetNameInvalid(uint32 error
, const std::string
& name
, DeclinedName
*declinedName
)
671 WorldPacket
data(SMSG_PET_NAME_INVALID
, 4 + name
.size() + 1 + 1);
672 data
<< uint32(error
);
677 for(uint32 i
= 0; i
< MAX_DECLINED_NAME_CASES
; ++i
)
678 data
<< declinedName
->name
[i
];
685 void WorldSession::HandlePetLearnTalent( WorldPacket
& recv_data
)
687 DEBUG_LOG("WORLD: CMSG_PET_LEARN_TALENT");
690 uint32 talent_id
, requested_rank
;
691 recv_data
>> guid
>> talent_id
>> requested_rank
;
693 _player
->LearnPetTalent(guid
, talent_id
, requested_rank
);
694 _player
->SendTalentsInfoData(true);
697 void WorldSession::HandleLearnPreviewTalentsPet( WorldPacket
& recv_data
)
699 DEBUG_LOG("CMSG_LEARN_PREVIEW_TALENTS_PET");
705 recv_data
>> talentsCount
;
707 uint32 talentId
, talentRank
;
709 for(uint32 i
= 0; i
< talentsCount
; ++i
)
711 recv_data
>> talentId
>> talentRank
;
713 _player
->LearnPetTalent(guid
, talentId
, talentRank
);
716 _player
->SendTalentsInfoData(true);