[9581] Fixed apply damage reduction to melee/ranged damage.
[getmangos.git] / src / game / PetHandler.cpp
blob9ae70b7ec6cbf9989dc6424d458f93f5e8ab698d
1 /*
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
19 #include "Common.h"
20 #include "WorldPacket.h"
21 #include "WorldSession.h"
22 #include "ObjectMgr.h"
23 #include "SpellMgr.h"
24 #include "Log.h"
25 #include "Opcodes.h"
26 #include "Spell.h"
27 #include "ObjectAccessor.h"
28 #include "CreatureAI.h"
29 #include "Util.h"
30 #include "Pet.h"
32 void WorldSession::HandlePetAction( WorldPacket & recv_data )
34 uint64 guid1;
35 uint32 data;
36 uint64 guid2;
37 recv_data >> guid1; //pet guid
38 recv_data >> data;
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 sLog.outDetail("HandlePetAction.Pet %u flag is %u, spellid is %u, target %u.", uint32(GUID_LOPART(guid1)), uint32(flag), spellid, uint32(GUID_LOPART(guid2)) );
47 if (!pet)
49 sLog.outError( "Pet %u not exist.", uint32(GUID_LOPART(guid1)) );
50 return;
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() );
56 return;
59 if (!pet->isAlive())
60 return;
62 if (pet->GetTypeId() == TYPEID_PLAYER)
64 // controller player can only do melee attack
65 if (!(flag == ACT_COMMAND && spellid == COMMAND_ATTACK))
66 return;
68 else if (((Creature*)pet)->isPet())
70 // pet can have action bar disabled
71 if(((Pet*)pet)->GetModeFlags() & PET_MODE_DISABLE_ACTIONS)
72 return;
75 CharmInfo *charmInfo = pet->GetCharmInfo();
76 if(!charmInfo)
78 sLog.outError("WorldSession::HandlePetAction: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId());
79 return;
82 switch(flag)
84 case ACT_COMMAND: //0x07
85 switch(spellid)
87 case COMMAND_STAY: //flat=1792 //STAY
88 pet->StopMoving();
89 pet->GetMotionMaster()->Clear();
90 pet->GetMotionMaster()->MoveIdle();
91 charmInfo->SetCommandState( COMMAND_STAY );
92 break;
93 case COMMAND_FOLLOW: //spellid=1792 //FOLLOW
94 pet->AttackStop();
95 pet->GetMotionMaster()->MoveFollow(_player,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE);
96 charmInfo->SetCommandState( COMMAND_FOLLOW );
97 break;
98 case COMMAND_ATTACK: //spellid=1792 //ATTACK
100 const uint64& selguid = _player->GetSelection();
101 Unit *TargetUnit = ObjectAccessor::GetUnit(*_player, selguid);
102 if(!TargetUnit)
103 return;
105 // not let attack friendly units.
106 if(GetPlayer()->IsFriendlyTo(TargetUnit))
107 return;
108 // Not let attack through obstructions
109 if(!pet->IsWithinLOSInMap(TargetUnit))
110 return;
112 // This is true if pet has no target or has target but targets differs.
113 if(pet->getVictim() != TargetUnit)
115 if (pet->getVictim())
116 pet->AttackStop();
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);
127 else
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);
139 break;
141 case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet)
142 if(((Creature*)pet)->isPet())
144 Pet* p = (Pet*)pet;
145 if(p->getPetType() == HUNTER_PET)
146 _player->RemovePet(p,PET_SAVE_AS_DELETED);
147 else
148 //dismissing a summoned pet is like killing them (this prevents returning a soulshard...)
149 p->setDeathState(CORPSE);
151 else // charmed
152 _player->Uncharm();
153 break;
154 default:
155 sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid);
157 break;
158 case ACT_REACTION: // 0x6
159 switch(spellid)
161 case REACT_PASSIVE: //passive
162 case REACT_DEFENSIVE: //recovery
163 case REACT_AGGRESSIVE: //activete
164 charmInfo->SetReactState( ReactStates(spellid) );
165 break;
167 break;
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)
174 return;
176 if(guid2)
177 unit_target = ObjectAccessor::GetUnit(*_player,guid2);
179 // do not cast unknown spells
180 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid );
181 if(!spellInfo)
183 sLog.outError("WORLD: unknown PET spell id %i", spellid);
184 return;
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)
190 return;
193 // do not cast not learned spells
194 if(!pet->HasSpell(spellid) || IsPassiveSpell(spellid))
195 return;
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))
206 if(unit_target)
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);
234 else
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())
245 pet->AttackStop();
246 pet->GetMotionMaster()->Clear();
247 if (((Creature*)pet)->AI())
248 ((Creature*)pet)->AI()->AttackStart(unit_target);
252 spell->prepare(&(spell->m_targets));
254 else
256 if(pet->HasAuraType(SPELL_AURA_MOD_POSSESS))
257 Spell::SendCastResult(GetPlayer(),spellInfo,0,result);
258 else
259 pet->SendPetCastFail(spellid, result);
261 if (!((Creature*)pet)->HasSpellCooldown(spellid))
262 GetPlayer()->SendClearCooldown(spellid, pet);
264 spell->finish(false);
265 delete spell;
267 break;
269 default:
270 sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid);
274 void WorldSession::HandlePetNameQuery( WorldPacket & recv_data )
276 sLog.outDetail( "HandlePetNameQuery. CMSG_PET_NAME_QUERY" );
278 uint32 petnumber;
279 uint64 petguid;
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)
291 return;
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() )
302 data << uint8(1);
303 for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
304 data << ((Pet*)pet)->GetDeclinedNames()->name[i];
306 else
307 data << uint8(0);
309 _player->GetSession()->SendPacket(&data);
312 void WorldSession::HandlePetSetAction( WorldPacket & recv_data )
314 sLog.outDetail( "HandlePetSetAction. CMSG_PET_SET_ACTION" );
316 uint64 petguid;
317 uint8 count;
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." );
326 return;
329 // pet can have action bar disabled
330 if(pet->isPet() && ((Pet*)pet)->GetModeFlags() & PET_MODE_DISABLE_ACTIONS)
331 return;
333 CharmInfo *charmInfo = pet->GetCharmInfo();
334 if(!charmInfo)
336 sLog.outError("WorldSession::HandlePetSetAction: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId());
337 return;
340 count = (recv_data.size() == 24) ? 2 : 1;
342 uint32 position[2];
343 uint32 data[2];
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)
355 return;
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)
362 if (count == 1)
363 return;
365 move_command = true;
369 // check swap (at command->spell swap client remove spell first in another packet, so check only command move correctness)
370 if (move_command)
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())
379 return;
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())
389 return;
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 sLog.outDetail( "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)))
403 //sign for autocast
404 if(act_state == ACT_ENABLED && spell_id)
406 if(pet->isCharmed())
407 charmInfo->ToggleCreatureAutocast(spell_id, true);
408 else
409 ((Pet*)pet)->ToggleAutocast(spell_id, true);
411 //sign for no/turn off autocast
412 else if(act_state == ACT_DISABLED && spell_id)
414 if(pet->isCharmed())
415 charmInfo->ToggleCreatureAutocast(spell_id, false);
416 else
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 sLog.outDetail( "HandlePetRename. CMSG_PET_RENAME" );
429 uint64 petguid;
430 uint8 isdeclined;
432 std::string name;
433 DeclinedName declinedname;
435 recv_data >> petguid;
436 recv_data >> name;
437 recv_data >> isdeclined;
439 Pet* pet = _player->GetMap()->GetPet(petguid);
440 // check it!
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() )
444 return;
446 PetNameInvalidReason res = ObjectMgr::CheckPetName(name);
447 if(res != PET_NAME_SUCCESS)
449 SendPetNameInvalid(res, name, NULL);
450 return;
453 if(sObjectMgr.IsReservedName(name))
455 SendPetNameInvalid(PET_NAME_RESERVED, name, NULL);
456 return;
459 pet->SetName(name);
461 if(_player->GetGroup())
462 _player->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_NAME);
464 pet->RemoveByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED);
466 if(isdeclined)
468 for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
470 recv_data >> declinedname.name[i];
473 std::wstring wname;
474 Utf8toWStr(name, wname);
475 if(!ObjectMgr::CheckDeclinedNames(GetMainPartOfName(wname,0),declinedname))
477 SendPetNameInvalid(PET_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME, name, &declinedname);
478 return;
482 CharacterDatabase.BeginTransaction();
483 if(isdeclined)
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 )
501 uint64 guid;
502 recv_data >> guid; //pet guid
503 sLog.outDetail( "HandlePetAbandon. CMSG_PET_ABANDON pet guid is %u", GUID_LOPART(guid) );
505 if(!_player->IsInWorld())
506 return;
508 // pet/charmed
509 if (Creature* pet = _player->GetMap()->GetCreatureOrPetOrVehicle(guid))
511 if(pet->isPet())
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())
523 _player->Uncharm();
528 void WorldSession::HandlePetUnlearnOpcode(WorldPacket& recvPacket)
530 sLog.outDetail("CMSG_PET_UNLEARN");
531 uint64 guid;
532 recvPacket >> guid; // Pet guid
534 Pet* pet = _player->GetPet();
536 if(!pet || pet->getPetType() != HUNTER_PET || pet->m_usedTalentCount == 0)
537 return;
539 if(guid != pet->GetGUID())
541 sLog.outError( "HandlePetUnlearnOpcode.Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() );
542 return;
545 CharmInfo *charmInfo = pet->GetCharmInfo();
546 if(!charmInfo)
548 sLog.outError("WorldSession::HandlePetUnlearnOpcode: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId());
549 return;
551 pet->resetTalents();
552 _player->SendTalentsInfoData(true);
555 void WorldSession::HandlePetSpellAutocastOpcode( WorldPacket& recvPacket )
557 sLog.outDetail("CMSG_PET_SPELL_AUTOCAST");
558 uint64 guid;
559 uint32 spellid;
560 uint8 state; //1 for on, 0 for off
561 recvPacket >> guid >> spellid >> state;
563 if(!_player->GetPet() && !_player->GetCharm())
564 return;
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() );
571 return;
574 // do not add not learned spells/ passive spells
575 if(!pet->HasSpell(spellid) || IsPassiveSpell(spellid))
576 return;
578 CharmInfo *charmInfo = pet->GetCharmInfo();
579 if(!charmInfo)
581 sLog.outError("WorldSession::HandlePetSpellAutocastOpcod: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId());
582 return;
585 if(pet->isCharmed())
586 //state can be used as boolean
587 pet->GetCharmInfo()->ToggleCreatureAutocast(spellid, state);
588 else
589 ((Pet*)pet)->ToggleAutocast(spellid, state);
591 charmInfo->SetSpellAutocast(spellid,state);
594 void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket )
596 sLog.outDetail("WORLD: CMSG_PET_CAST_SPELL");
598 uint64 guid;
599 uint32 spellid;
600 uint8 cast_count;
601 uint8 unk_flags; // flags (if 0x02 - some additional data are received)
603 recvPacket >> guid >> cast_count >> spellid >> unk_flags;
605 sLog.outDebug("WORLD: CMSG_PET_CAST_SPELL, cast_count: %u, spellid %u, unk_flags %u", cast_count, spellid, unk_flags);
607 if (!_player->GetPet() && !_player->GetCharm())
608 return;
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() );
615 return;
618 if (pet->GetGlobalCooldown() > 0)
619 return;
621 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid);
622 if (!spellInfo)
624 sLog.outError("WORLD: unknown PET spell id %i", spellid);
625 return;
628 // do not cast not learned spells
629 if (!pet->HasSpell(spellid) || IsPassiveSpell(spellid))
630 return;
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);
646 if (pet->isPet())
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);
652 else
653 pet->SendPetAIReaction(guid);
656 spell->prepare(&(spell->m_targets));
658 else
660 pet->SendPetCastFail(spellid, result);
661 if (!pet->HasSpellCooldown(spellid))
662 GetPlayer()->SendClearCooldown(spellid, pet);
664 spell->finish(false);
665 delete spell;
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);
673 data << name;
674 if(declinedName)
676 data << uint8(1);
677 for(uint32 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
678 data << declinedName->name[i];
680 else
681 data << uint8(0);
682 SendPacket(&data);
685 void WorldSession::HandlePetLearnTalent( WorldPacket & recv_data )
687 sLog.outDebug("WORLD: CMSG_PET_LEARN_TALENT");
689 uint64 guid;
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 sLog.outDebug("CMSG_LEARN_PREVIEW_TALENTS_PET");
701 uint64 guid;
702 recv_data >> guid;
704 uint32 talentsCount;
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);