TrinityCore
Loading...
Searching...
No Matches
PetHandler.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "WorldSession.h"
19#include "Common.h"
20#include "CreatureAI.h"
21#include "DatabaseEnv.h"
22#include "Group.h"
23#include "Log.h"
24#include "MotionMaster.h"
25#include "ObjectAccessor.h"
26#include "ObjectMgr.h"
27#include "Opcodes.h"
28#include "Pet.h"
29#include "PetAI.h"
30#include "PetPackets.h"
31#include "Player.h"
32#include "Spell.h"
33#include "SpellHistory.h"
34#include "SpellInfo.h"
35#include "SpellMgr.h"
36#include "SpellPackets.h"
37#include "Util.h"
38#include "World.h"
39#include "WorldPacket.h"
40
42{
44
45 if (!pet)
46 {
47 TC_LOG_DEBUG("entities.pet", "Vanitypet ({}) does not exist - player '{}' ({} / account: {}) attempted to dismiss it (possibly lagged out)",
48 packet.CritterGUID.ToString(), GetPlayer()->GetName(), GetPlayer()->GetGUID().ToString(), GetAccountId());
49 return;
50 }
51
52 if (_player->GetCritterGUID() == pet->GetGUID())
53 {
54 if (pet->GetTypeId() == TYPEID_UNIT && pet->IsSummon())
55 pet->ToTempSummon()->UnSummon();
56 }
57}
58
60{
61 ObjectGuid guid1;
62 uint32 data;
63 ObjectGuid guid2;
64 recvData >> guid1; // pet guid
65 recvData >> data;
66 recvData >> guid2; // tag guid
67
68 uint32 spellid = UNIT_ACTION_BUTTON_ACTION(data);
69 uint8 flag = UNIT_ACTION_BUTTON_TYPE(data); // delete = 0x07 CastSpell = C1
70
71 // used also for charmed creature
72 Unit* pet = ObjectAccessor::GetUnit(*_player, guid1);
73 TC_LOG_DEBUG("entities.pet", "HandlePetAction: {} - flag: {}, spellid: {}, target: {}.", guid1.ToString(), uint32(flag), spellid, guid2.ToString());
74
75 if (!pet)
76 {
77 TC_LOG_DEBUG("entities.pet", "HandlePetAction: {} doesn't exist for {} {}", guid1.ToString(), GetPlayer()->GetGUID().ToString(), GetPlayer()->GetName());
78 return;
79 }
80
81 if (pet != GetPlayer()->GetFirstControlled())
82 {
83 TC_LOG_DEBUG("entities.pet", "HandlePetAction: {} does not belong to {} {}", guid1.ToString(), GetPlayer()->GetGUID().ToString(), GetPlayer()->GetName());
84 return;
85 }
86
87 if (!pet->IsAlive())
88 {
89 SpellInfo const* spell = (flag == ACT_ENABLED || flag == ACT_PASSIVE) ? sSpellMgr->GetSpellInfo(spellid) : nullptr;
90 if (!spell)
91 return;
93 return;
94 }
95
97 if (pet->GetTypeId() == TYPEID_PLAYER && !(flag == ACT_COMMAND && spellid == COMMAND_ATTACK))
98 return;
99
100 if (GetPlayer()->m_Controlled.size() == 1)
101 HandlePetActionHelper(pet, guid1, spellid, flag, guid2);
102 else
103 {
104 // If a pet is dismissed, m_Controlled will change
105 std::vector<Unit*> controlled;
106 for (Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr)
107 if ((*itr)->GetEntry() == pet->GetEntry() && (*itr)->IsAlive())
108 controlled.push_back(*itr);
109 for (std::vector<Unit*>::iterator itr = controlled.begin(); itr != controlled.end(); ++itr)
110 HandlePetActionHelper(*itr, guid1, spellid, flag, guid2);
111 }
112}
113
115{
117
118 if (!pet)
119 {
120 TC_LOG_ERROR("entities.pet", "HandlePetStopAttack: {} does not exist", packet.PetGUID.ToString());
121 return;
122 }
123
124 if (pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharmed())
125 {
126 TC_LOG_ERROR("entities.pet", "HandlePetStopAttack: {} isn't a pet or charmed creature of player {}",
127 packet.PetGUID.ToString(), GetPlayer()->GetName());
128 return;
129 }
130
131 if (!pet->IsAlive())
132 return;
133
134 pet->AttackStop();
135}
136
138{
139 CharmInfo* charmInfo = pet->GetCharmInfo();
140 if (!charmInfo)
141 {
142 TC_LOG_DEBUG("entities.pet", "WorldSession::HandlePetAction(petGuid: {}, tagGuid: {}, spellId: {}, flag: {}): object {} is considered pet-like but doesn't have a charminfo!",
143 guid1.ToString(), guid2.ToString(), spellid, flag, pet->GetGUID().ToString());
144 return;
145 }
146
147 switch (flag)
148 {
149 case ACT_COMMAND: // 0x07
150 switch (spellid)
151 {
152 case COMMAND_STAY: // flat = 1792 - STAY
154 pet->GetMotionMaster()->MoveIdle();
155
156 charmInfo->SetCommandState(COMMAND_STAY);
157 charmInfo->SetIsCommandAttack(false);
158 charmInfo->SetIsAtStay(true);
159 charmInfo->SetIsCommandFollow(false);
160 charmInfo->SetIsFollowing(false);
161 charmInfo->SetIsReturning(false);
162 charmInfo->SaveStayPosition();
163 break;
164 case COMMAND_FOLLOW: // spellid = 1792 - FOLLOW
165 pet->AttackStop();
166 pet->InterruptNonMeleeSpells(false);
168
170 charmInfo->SetIsCommandAttack(false);
171 charmInfo->SetIsAtStay(false);
172 charmInfo->SetIsReturning(true);
173 charmInfo->SetIsCommandFollow(true);
174 charmInfo->SetIsFollowing(false);
175 break;
176 case COMMAND_ATTACK: // spellid = 1792 - ATTACK
177 {
178 // only place where pet can be player
179 Unit* TargetUnit = ObjectAccessor::GetUnit(*_player, guid2);
180 if (!TargetUnit)
181 return;
182
183 if (Unit* owner = pet->GetOwner())
184 if (!owner->IsValidAttackTarget(TargetUnit))
185 return;
186
187 // This is true if pet has no target or has target but targets differs.
188 if (pet->GetVictim() != TargetUnit || !pet->GetCharmInfo()->IsCommandAttack())
189 {
190 if (pet->GetVictim())
191 pet->AttackStop();
192
193 if (pet->GetTypeId() != TYPEID_PLAYER && pet->ToCreature()->IsAIEnabled())
194 {
195 charmInfo->SetIsCommandAttack(true);
196 charmInfo->SetIsAtStay(false);
197 charmInfo->SetIsFollowing(false);
198 charmInfo->SetIsCommandFollow(false);
199 charmInfo->SetIsReturning(false);
200
201 CreatureAI* AI = pet->ToCreature()->AI();
202 if (PetAI* petAI = dynamic_cast<PetAI*>(AI))
203 petAI->_AttackStart(TargetUnit); // force target switch
204 else
205 AI->AttackStart(TargetUnit);
206
207 // 10% chance to play special pet attack talk, else growl
208 if (pet->IsPet() && pet->ToPet()->getPetType() == SUMMON_PET && pet != TargetUnit && roll_chance_i(10))
210 else
211 {
212 // 90% chance for pet and 100% chance for charmed creature
213 pet->SendPetAIReaction(guid1);
214 }
215 }
216 else // charmed player
217 {
218 charmInfo->SetIsCommandAttack(true);
219 charmInfo->SetIsAtStay(false);
220 charmInfo->SetIsFollowing(false);
221 charmInfo->SetIsCommandFollow(false);
222 charmInfo->SetIsReturning(false);
223
224 pet->Attack(TargetUnit, true);
225 pet->SendPetAIReaction(guid1);
226 }
227 }
228 break;
229 }
230 case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet)
231 if (pet->GetCharmerGUID() == GetPlayer()->GetGUID())
233 else if (pet->GetOwnerGUID() == GetPlayer()->GetGUID())
234 {
235 ASSERT(pet->GetTypeId() == TYPEID_UNIT);
236 if (pet->IsPet())
237 {
238 if (pet->ToPet()->getPetType() == HUNTER_PET)
240 else
241 {
242 pet->SendPetDismissSound();
244 }
245 }
246 else if (pet->HasUnitTypeMask(UNIT_MASK_MINION))
247 {
248 ((Minion*)pet)->UnSummon();
249 }
250 }
251 break;
252 default:
253 TC_LOG_ERROR("entities.pet", "WORLD: unknown PET flag Action {} and spellid {}.", uint32(flag), spellid);
254 }
255 break;
256 case ACT_REACTION: // 0x6
257 switch (spellid)
258 {
259 case REACT_PASSIVE: // passive
260 pet->AttackStop();
261 [[fallthrough]];
262 case REACT_DEFENSIVE: // recovery
263 case REACT_AGGRESSIVE: // activete
264 if (pet->GetTypeId() == TYPEID_UNIT)
265 pet->ToCreature()->SetReactState(ReactStates(spellid));
266 break;
267 }
268 break;
269 case ACT_DISABLED: // 0x81 spell (disabled), ignore
270 case ACT_PASSIVE: // 0x01
271 case ACT_ENABLED: // 0xC1 spell
272 {
273 Unit* unit_target = nullptr;
274
275 if (!guid2.IsEmpty())
276 unit_target = ObjectAccessor::GetUnit(*_player, guid2);
277
278 // do not cast unknown spells
279 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid);
280 if (!spellInfo)
281 {
282 TC_LOG_ERROR("spells.pet", "WORLD: unknown PET spell id {}", spellid);
283 return;
284 }
285
286 for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects())
287 {
288 if (spellEffectInfo.TargetA.GetTarget() == TARGET_UNIT_SRC_AREA_ENEMY || spellEffectInfo.TargetA.GetTarget() == TARGET_UNIT_DEST_AREA_ENEMY || spellEffectInfo.TargetA.GetTarget() == TARGET_DEST_DYNOBJ_ENEMY)
289 return;
290 }
291
292 // do not cast not learned spells
293 if (!pet->HasSpell(spellid) || spellInfo->IsPassive())
294 return;
295
296 // Clear the flags as if owner clicked 'attack'. AI will reset them
297 // after AttackStart, even if spell failed
298 if (pet->GetCharmInfo())
299 {
300 pet->GetCharmInfo()->SetIsAtStay(false);
301 pet->GetCharmInfo()->SetIsCommandAttack(true);
302 pet->GetCharmInfo()->SetIsReturning(false);
303 pet->GetCharmInfo()->SetIsFollowing(false);
304 }
305
306 Spell* spell = new Spell(pet, spellInfo, TRIGGERED_NONE);
307
308 SpellCastResult result = spell->CheckPetCast(unit_target);
309
310 // auto turn to target unless possessed
311 if (result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->isPossessed() && !pet->IsVehicle())
312 {
313 if (unit_target)
314 {
315 if (!pet->HasSpellFocus())
316 pet->SetInFront(unit_target);
317 if (Player* player = unit_target->ToPlayer())
318 pet->SendUpdateToPlayer(player);
319 }
320 else if (Unit* unit_target2 = spell->m_targets.GetUnitTarget())
321 {
322 if (!pet->HasSpellFocus())
323 pet->SetInFront(unit_target2);
324 if (Player* player = unit_target2->ToPlayer())
325 pet->SendUpdateToPlayer(player);
326 }
327
328 if (Unit* powner = pet->GetCharmerOrOwner())
329 if (Player* player = powner->ToPlayer())
330 pet->SendUpdateToPlayer(player);
331
332 result = SPELL_CAST_OK;
333 }
334
335 if (result == SPELL_CAST_OK)
336 {
337 unit_target = spell->m_targets.GetUnitTarget();
338
339 // 10% chance to play special pet attack talk, else growl
340 // actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
341 if (pet->IsPet() && pet->ToPet()->getPetType() == SUMMON_PET && pet != unit_target && roll_chance_i(10))
343 else
344 {
345 pet->SendPetAIReaction(guid1);
346 }
347
348 if (unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->isPossessed() && !pet->IsVehicle())
349 {
350 // This is true if pet has no target or has target but targets differs.
351 if (pet->GetVictim() != unit_target)
352 {
353 if (CreatureAI* AI = pet->ToCreature()->AI())
354 {
355 if (PetAI* petAI = dynamic_cast<PetAI*>(AI))
356 petAI->_AttackStart(unit_target); // force victim switch
357 else
358 AI->AttackStart(unit_target);
359 }
360 }
361 }
362
363 spell->prepare(spell->m_targets);
364 }
365 else
366 {
367 if (pet->isPossessed() || pet->IsVehicle())
368 Spell::SendCastResult(GetPlayer(), spellInfo, 0, result);
369 else
370 spell->SendPetCastResult(result);
371
372 if (!pet->GetSpellHistory()->HasCooldown(spellid))
373 pet->GetSpellHistory()->ResetCooldown(spellid, true);
374
375 spell->finish(false);
376 delete spell;
377
378 // reset specific flags in case of spell fail. AI will reset other flags
379 if (pet->GetCharmInfo())
380 pet->GetCharmInfo()->SetIsCommandAttack(false);
381 }
382 break;
383 }
384 default:
385 TC_LOG_ERROR("entities.pet", "WORLD: unknown PET flag Action {} and spellid {}.", uint32(flag), spellid);
386 }
387}
388
390{
391 TC_LOG_DEBUG("network.opcode", "WORLD: Received CMSG_PET_NAME_QUERY");
392
393 uint32 petnumber;
394 ObjectGuid petguid;
395
396 recvData >> petnumber;
397 recvData >> petguid;
398
399 SendQueryPetNameResponse(petguid, petnumber);
400}
401
403{
405 if (!pet)
406 {
408 data << uint32(petnumber);
409 data << uint8(0);
410 data << uint32(0);
411 data << uint8(0);
413 return;
414 }
415
416 WorldPacket data(SMSG_PET_NAME_QUERY_RESPONSE, (4+4+pet->GetName().size()+1));
417 data << uint32(petnumber);
418 data << pet->GetName();
420
421 if (pet->IsPet() && ((Pet*)pet)->GetDeclinedNames())
422 {
423 data << uint8(1);
424 for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
425 data << ((Pet*)pet)->GetDeclinedNames()->name[i];
426 }
427 else
428 data << uint8(0);
429
431}
432
434{
435 // spell case or GM
436 if (guid == GetPlayer()->GetGUID())
437 {
438 if (!GetPlayer()->IsGameMaster() && !GetPlayer()->HasAuraType(SPELL_AURA_OPEN_STABLE))
439 {
440 TC_LOG_DEBUG("entities.player.cheat", "{} attempt open stable in cheating way.", guid.ToString());
441 return false;
442 }
443 }
444 // stable master case
445 else
446 {
447 if (!GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_STABLEMASTER))
448 {
449 TC_LOG_DEBUG("entities.player", "Stablemaster {} not found or you can't interact with him.", guid.ToString());
450 return false;
451 }
452 }
453 return true;
454}
455
457{
458 TC_LOG_DEBUG("network.opcode", "WORLD: Received CMSG_PET_SET_ACTION");
459
460 ObjectGuid petguid;
461 uint8 count;
462
463 recvData >> petguid;
464
465 Unit* pet = ObjectAccessor::GetUnit(*_player, petguid);
466
467 if (!pet || pet != _player->GetFirstControlled())
468 {
469 TC_LOG_ERROR("entities.pet", "HandlePetSetAction: Unknown {} or owner ({})", petguid.ToString(), _player->GetGUID().ToString());
470 return;
471 }
472
473 CharmInfo* charmInfo = pet->GetCharmInfo();
474 if (!charmInfo)
475 {
476 TC_LOG_ERROR("entities.pet", "WorldSession::HandlePetSetAction: object {} is considered pet-like but doesn't have a charminfo!", pet->GetGUID().ToString());
477 return;
478 }
479
480 count = (recvData.size() == 24) ? 2 : 1;
481
482 uint32 position[2];
483 uint32 data[2];
484 bool move_command = false;
485
486 for (uint8 i = 0; i < count; ++i)
487 {
488 recvData >> position[i];
489 recvData >> data[i];
490
491 uint8 act_state = UNIT_ACTION_BUTTON_TYPE(data[i]);
492
493 // ignore invalid position
494 if (position[i] >= MAX_UNIT_ACTION_BAR_INDEX)
495 return;
496
497 // in the normal case, command and reaction buttons can only be moved, not removed
498 // at moving count == 2, at removing count == 1
499 // ignore attempt to remove command|reaction buttons (not possible at normal case)
500 if (act_state == ACT_COMMAND || act_state == ACT_REACTION)
501 {
502 if (count == 1)
503 return;
504
505 move_command = true;
506 }
507 }
508
509 std::vector<Unit*> pets;
510 for (Unit* controlled : _player->m_Controlled)
511 if (controlled->GetEntry() == pet->GetEntry() && controlled->IsAlive())
512 pets.push_back(controlled);
513
514 for (Unit* petControlled : pets)
515 {
516 // check swap (at command->spell swap client remove spell first in another packet, so check only command move correctness)
517 if (move_command)
518 {
519 uint8 act_state_0 = UNIT_ACTION_BUTTON_TYPE(data[0]);
520 if (act_state_0 == ACT_COMMAND || act_state_0 == ACT_REACTION)
521 {
522 uint32 spell_id_0 = UNIT_ACTION_BUTTON_ACTION(data[0]);
523 UnitActionBarEntry const* actionEntry_1 = charmInfo->GetActionBarEntry(position[1]);
524 if (!actionEntry_1 || spell_id_0 != actionEntry_1->GetAction() ||
525 act_state_0 != actionEntry_1->GetType())
526 return;
527 }
528
529 uint8 act_state_1 = UNIT_ACTION_BUTTON_TYPE(data[1]);
530 if (act_state_1 == ACT_COMMAND || act_state_1 == ACT_REACTION)
531 {
532 uint32 spell_id_1 = UNIT_ACTION_BUTTON_ACTION(data[1]);
533 UnitActionBarEntry const* actionEntry_0 = charmInfo->GetActionBarEntry(position[0]);
534 if (!actionEntry_0 || spell_id_1 != actionEntry_0->GetAction() ||
535 act_state_1 != actionEntry_0->GetType())
536 return;
537 }
538 }
539
540 for (uint8 i = 0; i < count; ++i)
541 {
542 uint32 spell_id = UNIT_ACTION_BUTTON_ACTION(data[i]);
543 uint8 act_state = UNIT_ACTION_BUTTON_TYPE(data[i]);
544
545 TC_LOG_DEBUG("entities.pet", "Player {} has changed pet spell action. Position: {}, Spell: {}, State: 0x{:X}",
546 _player->GetName(), position[i], spell_id, uint32(act_state));
547
548 // 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
549 if (!((act_state == ACT_ENABLED || act_state == ACT_DISABLED || act_state == ACT_PASSIVE) && spell_id && !petControlled->HasSpell(spell_id)))
550 {
551 if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id))
552 {
553 // sign for autocast
554 if (act_state == ACT_ENABLED)
555 {
556 if (petControlled->GetTypeId() == TYPEID_UNIT && petControlled->IsPet())
557 ((Pet*)petControlled)->ToggleAutocast(spellInfo, true);
558 else
559 for (Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr)
560 if ((*itr)->GetEntry() == petControlled->GetEntry())
561 (*itr)->GetCharmInfo()->ToggleCreatureAutocast(spellInfo, true);
562 }
563 // sign for no/turn off autocast
564 else if (act_state == ACT_DISABLED)
565 {
566 if (petControlled->GetTypeId() == TYPEID_UNIT && petControlled->IsPet())
567 ((Pet*)petControlled)->ToggleAutocast(spellInfo, false);
568 else
569 for (Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr)
570 if ((*itr)->GetEntry() == petControlled->GetEntry())
571 (*itr)->GetCharmInfo()->ToggleCreatureAutocast(spellInfo, false);
572 }
573 }
574
575 charmInfo->SetActionBar(position[i], spell_id, ActiveStates(act_state));
576 }
577 }
578 }
579}
580
582{
583 TC_LOG_DEBUG("network.opcode", "WORLD: Received CMSG_PET_RENAME");
584
585 ObjectGuid petguid;
586 uint8 isdeclined;
587
588 std::string name;
589 DeclinedName declinedname;
590
591 recvData >> petguid;
592 recvData >> name;
593 recvData >> isdeclined;
594
595 PetStable* petStable = _player->GetPetStable();
596 Pet* pet = ObjectAccessor::GetPet(*_player, petguid);
597 if (!pet || !pet->IsPet() || ((Pet*)pet)->getPetType() != HUNTER_PET || !pet->HasPetFlag(UNIT_PET_FLAG_CAN_BE_RENAMED) ||
598 pet->GetOwnerGUID() != _player->GetGUID() || !pet->GetCharmInfo() ||
599 !petStable || !petStable->CurrentPet || petStable->CurrentPet->PetNumber != pet->GetCharmInfo()->GetPetNumber())
600 return;
601
603 if (res != PET_NAME_SUCCESS)
604 {
605 SendPetNameInvalid(res, name, nullptr);
606 return;
607 }
608
609 if (sObjectMgr->IsReservedName(name))
610 {
612 return;
613 }
614
615 pet->SetName(name);
616
617 if (pet->GetOwner()->GetGroup())
619
621
622 petStable->CurrentPet->Name = name;
623 petStable->CurrentPet->WasRenamed = true;
624
625 if (isdeclined)
626 {
627 for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
628 {
629 recvData >> declinedname.name[i];
630 }
631
632 std::wstring wname;
633 if (!Utf8toWStr(name, wname))
634 return;
635
636 if (!ObjectMgr::CheckDeclinedNames(wname, declinedname))
637 {
639 return;
640 }
641 }
642
643 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
644 if (isdeclined)
645 {
647 stmt->setUInt32(0, pet->GetCharmInfo()->GetPetNumber());
648 trans->Append(stmt);
649
650 stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_PET_DECLINEDNAME);
651 stmt->setUInt32(0, pet->GetCharmInfo()->GetPetNumber());
652 stmt->setUInt32(1, _player->GetGUID().GetCounter());
653
654 for (uint8 i = 0; i < 5; i++)
655 stmt->setString(i + 2, declinedname.name[i]);
656
657 trans->Append(stmt);
658 }
659
661 stmt->setString(0, name);
662 stmt->setUInt32(1, _player->GetGUID().GetCounter());
663 stmt->setUInt32(2, pet->GetCharmInfo()->GetPetNumber());
664 trans->Append(stmt);
665
666 CharacterDatabase.CommitTransaction(trans);
667
668 pet->SetPetNameTimestamp(uint32(GameTime::GetGameTime())); // cast can't be helped
669}
670
672{
673 if (!_player->IsInWorld())
674 return;
675
676 // pet/charmed
678 if (pet && pet->ToPet() && pet->ToPet()->getPetType() == HUNTER_PET)
679 {
680 if (pet->GetGUID() == _player->GetPetGUID())
681 {
682 uint32 feelty = pet->GetPower(POWER_HAPPINESS);
683 pet->SetPower(POWER_HAPPINESS, feelty > 50000 ? (feelty-50000) : 0);
684 }
685
687 }
688}
689
691{
693 if (!pet)
694 {
695 TC_LOG_ERROR("entities.pet", "WorldSession::HandlePetSpellAutocastOpcode: Pet {} not found.", packet.PetGUID.ToString());
696 return;
697 }
698
699 if (pet != _player->GetGuardianPet() && pet != _player->GetCharmed())
700 {
701 TC_LOG_ERROR("entities.pet", "WorldSession::HandlePetSpellAutocastOpcode: {} isn't pet of player {} ({}).",
702 packet.PetGUID.ToString(), GetPlayer()->GetName(), GetPlayer()->GetGUID().ToString());
703 return;
704 }
705
706 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(packet.SpellID);
707 if (!spellInfo)
708 {
709 TC_LOG_ERROR("spells.pet", "WorldSession::HandlePetSpellAutocastOpcode: Unknown spell id {} used by {}.", packet.SpellID, packet.PetGUID.ToString());
710 return;
711 }
712
713 std::vector<Unit*> pets;
714 for (Unit* controlled : _player->m_Controlled)
715 if (controlled->GetEntry() == pet->GetEntry() && controlled->IsAlive())
716 pets.push_back(controlled);
717
718 for (Unit* petControlled : pets)
719 {
720 // do not add not learned spells/ passive spells
721 if (!petControlled->HasSpell(packet.SpellID) || !spellInfo->IsAutocastable())
722 return;
723
724 CharmInfo* charmInfo = petControlled->GetCharmInfo();
725 if (!charmInfo)
726 {
727 TC_LOG_ERROR("entities.pet", "WorldSession::HandlePetSpellAutocastOpcode: object {} is considered pet-like but doesn't have a charminfo!", petControlled->GetGUID().ToString());
728 return;
729 }
730
731 if (Pet* summon = petControlled->ToPet())
732 summon->ToggleAutocast(spellInfo, packet.AutocastEnabled);
733 else
734 charmInfo->ToggleCreatureAutocast(spellInfo, packet.AutocastEnabled);
735
736 charmInfo->SetSpellAutocast(spellInfo, packet.AutocastEnabled);
737 }
738}
739
741{
742 // This opcode is also sent from charmed and possessed units (players and creatures)
744 return;
745
746 Unit* caster = ObjectAccessor::GetUnit(*_player, petCastSpell.PetGUID);
747
748 if (!caster || (caster != _player->GetGuardianPet() && caster != _player->GetCharmed()))
749 {
750 TC_LOG_ERROR("entities.pet", "HandlePetCastSpellOpcode: {} isn't pet of player {} ({}).", petCastSpell.PetGUID.ToString(), GetPlayer()->GetName(), GetPlayer()->GetGUID().ToString());
751 return;
752 }
753
754 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(petCastSpell.Cast.SpellID);
755 if (!spellInfo)
756 {
757 TC_LOG_ERROR("spells.pet", "WorldSession::HandlePetCastSpellOpcode: unknown spell id {} tried to cast by {}",
758 petCastSpell.Cast.SpellID, petCastSpell.PetGUID.ToString());
759 return;
760 }
761
762 SpellCastTargets targets(caster, petCastSpell.Cast);
763
764 TriggerCastFlags triggerCastFlags = TRIGGERED_NONE;
765
766 if (spellInfo->IsPassive())
767 return;
768
769 // cast only learned spells
770 if (!caster->HasSpell(spellInfo->Id))
771 {
772 bool allow = false;
773
774 // allow casting of spells triggered by clientside periodic trigger auras
776 {
777 allow = true;
778 triggerCastFlags = TRIGGERED_FULL_MASK;
779 }
780
781 if (!allow)
782 return;
783 }
784
785 if (petCastSpell.Cast.MissileTrajectory && petCastSpell.Cast.MissileTrajectory->MoveUpdate)
786 HandleMovementOpcode(MSG_MOVE_STOP, *petCastSpell.Cast.MissileTrajectory->MoveUpdate);
787
788 Spell* spell = new Spell(caster, spellInfo, triggerCastFlags);
789 spell->m_fromClient = true;
790 spell->m_cast_count = petCastSpell.Cast.CastID;
791 spell->InitExplicitTargets(targets);
792
793 SpellCastResult result = spell->CheckPetCast(nullptr);
794
795 if (result == SPELL_CAST_OK)
796 {
797 if (Creature* creature = caster->ToCreature())
798 {
799 if (Pet* pet = creature->ToPet())
800 {
801 // 10% chance to play special pet attack sound, else growl
802 // actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
803 if (pet->getPetType() == SUMMON_PET && roll_chance_i(10))
804 pet->SendPetActionSound(PET_ACTION_SPECIAL_SPELL);
805 else
806 pet->SendPetAIReaction(petCastSpell.PetGUID);
807 }
808 }
809
810 spell->prepare(spell->m_targets);
811 }
812 else
813 {
814 spell->SendPetCastResult(result);
815
816 if (!caster->GetSpellHistory()->HasCooldown(spellInfo->Id))
817 caster->GetSpellHistory()->ResetCooldown(spellInfo->Id, true);
818
819 spell->finish(false);
820 delete spell;
821 }
822}
823
824void WorldSession::SendPetNameInvalid(uint32 error, const std::string& name, DeclinedName *declinedName)
825{
826 WorldPacket data(SMSG_PET_NAME_INVALID, 4 + name.size() + 1 + 1);
827 data << uint32(error);
828 data << name;
829 if (declinedName)
830 {
831 data << uint8(1);
832 for (uint32 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
833 data << declinedName->name[i];
834 }
835 else
836 data << uint8(0);
837 SendPacket(&data);
838}
839
841{
842 TC_LOG_DEBUG("network.opcode", "WORLD: Received CMSG_PET_LEARN_TALENT");
843
844 ObjectGuid guid;
845 uint32 talentId, requestedRank;
846 recvData >> guid >> talentId >> requestedRank;
847
848 _player->LearnPetTalent(guid, talentId, requestedRank);
850}
851
853{
854 TC_LOG_DEBUG("network.opcode", "WORLD: Received CMSG_LEARN_PREVIEW_TALENTS_PET");
855
856 ObjectGuid guid;
857 recvData >> guid;
858
859 uint32 talentsCount;
860 recvData >> talentsCount;
861
862 uint32 talentId, talentRank;
863
864 // Client has max 24 talents, rounded up : 30
865 uint32 const MaxTalentsCount = 30;
866
867 for (uint32 i = 0; i < talentsCount && i < MaxTalentsCount; ++i)
868 {
869 recvData >> talentId >> talentRank;
870
871 _player->LearnPetTalent(guid, talentId, talentRank);
872 }
873
875
876 recvData.rfinish();
877}
878
880{
881 // Handle the packet CMSG_REQUEST_PET_INFO - sent when player does ingame /reload command
882
883 // Packet sent when player has a pet
884 if (_player->GetPet())
886 else if (Unit* charm = _player->GetCharmed())
887 {
888 // Packet sent when player has a possessed unit
889 if (charm->HasUnitState(UNIT_STATE_POSSESSED))
891 // Packet sent when player controlling a vehicle
892 else if (charm->HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED) && charm->HasUnitFlag(UNIT_FLAG_POSSESSED))
894 // Packet sent when player has a charmed unit
895 else
897 }
898}
@ CHAR_UPD_CHAR_PET_NAME
@ CHAR_INS_CHAR_PET_DECLINEDNAME
@ CHAR_DEL_CHAR_PET_DECLINEDNAME
SQLTransaction< CharacterDatabaseConnection > CharacterDatabaseTransaction
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
uint8_t uint8
Definition Define.h:135
uint16_t uint16
Definition Define.h:134
uint32_t uint32
Definition Define.h:133
#define ASSERT
Definition Errors.h:68
@ GROUP_UPDATE_FLAG_PET_NAME
Definition Group.h:112
#define TC_LOG_DEBUG(filterType__,...)
Definition Log.h:156
#define TC_LOG_ERROR(filterType__,...)
Definition Log.h:165
@ MOTION_PRIORITY_NORMAL
@ TYPEID_UNIT
Definition ObjectGuid.h:38
@ TYPEID_PLAYER
Definition ObjectGuid.h:39
#define sObjectMgr
Definition ObjectMgr.h:1721
@ PET_SAVE_AS_DELETED
Definition PetDefines.h:41
@ PET_SAVE_NOT_IN_SLOT
Definition PetDefines.h:45
#define PET_FOLLOW_DIST
Definition PetDefines.h:85
@ PET_ACTION_ATTACK
Definition PetDefines.h:82
@ PET_ACTION_SPECIAL_SPELL
Definition PetDefines.h:81
@ SUMMON_PET
Definition PetDefines.h:31
@ HUNTER_PET
Definition PetDefines.h:32
bool roll_chance_i(int chance)
Definition Random.h:59
@ TARGET_DEST_DYNOBJ_ENEMY
@ TARGET_UNIT_DEST_AREA_ENEMY
@ TARGET_UNIT_SRC_AREA_ENEMY
@ POWER_HAPPINESS
@ SPELL_ATTR0_CASTABLE_WHILE_DEAD
PetNameInvalidReason
@ PET_NAME_RESERVED
@ PET_NAME_SUCCESS
@ PET_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME
SpellCastResult
@ SPELL_FAILED_UNIT_NOT_INFRONT
@ SPELL_CAST_OK
@ SPELL_AURA_OPEN_STABLE
@ SPELL_AURA_PERIODIC_TRIGGER_SPELL_FROM_CLIENT
TriggerCastFlags
@ TRIGGERED_FULL_MASK
Will return SPELL_FAILED_DONT_REPORT in CheckCast functions.
@ TRIGGERED_NONE
#define sSpellMgr
Definition SpellMgr.h:738
@ UNIT_PET_FLAG_CAN_BE_RENAMED
ReactStates
@ REACT_DEFENSIVE
@ REACT_PASSIVE
@ REACT_AGGRESSIVE
#define MAX_DECLINED_NAME_CASES
ActiveStates
@ ACT_REACTION
@ ACT_COMMAND
@ ACT_ENABLED
@ ACT_PASSIVE
@ ACT_DISABLED
@ UNIT_NPC_FLAG_STABLEMASTER
@ COMMAND_ATTACK
@ COMMAND_ABANDON
@ COMMAND_STAY
@ COMMAND_FOLLOW
@ UNIT_FLAG_POSSESSED
@ UNIT_FLAG_PLAYER_CONTROLLED
@ UNIT_MASK_MINION
Definition Unit.h:379
#define MAX_UNIT_ACTION_BAR_INDEX
Definition Unit.h:684
#define UNIT_ACTION_BUTTON_ACTION(X)
Definition Unit.h:629
@ UNIT_STATE_POSSESSED
Definition Unit.h:250
#define UNIT_ACTION_BUTTON_TYPE(X)
Definition Unit.h:630
@ UNIT_FIELD_PET_NAME_TIMESTAMP
bool Utf8toWStr(char const *utf8str, size_t csize, wchar_t *wstr, size_t &wsize)
Definition Util.cpp:383
void rfinish()
Definition ByteBuffer.h:320
size_t size() const
Definition ByteBuffer.h:413
void SetReactState(ReactStates st)
Definition Creature.h:128
CreatureAI * AI() const
Definition Creature.h:163
void MoveFollow(Unit *target, float dist, ChaseAngle angle, MovementSlot slot=MOTION_SLOT_ACTIVE)
LowType GetCounter() const
Definition ObjectGuid.h:156
bool IsEmpty() const
Definition ObjectGuid.h:172
std::string ToString() const
static bool CheckDeclinedNames(const std::wstring &w_ownname, DeclinedName const &names)
static PetNameInvalidReason CheckPetName(std::string_view name, LocaleConstant locale)
static Creature * ToCreature(Object *o)
Definition Object.h:189
uint32 GetUInt32Value(uint16 index) const
Definition Object.cpp:241
bool IsInWorld() const
Definition Object.h:73
TypeID GetTypeId() const
Definition Object.h:93
uint32 GetEntry() const
Definition Object.h:81
static ObjectGuid GetGUID(Object const *o)
Definition Object.h:78
void SendUpdateToPlayer(Player *player)
Definition Object.cpp:192
static Player * ToPlayer(Object *o)
Definition Object.h:183
Definition PetAI.h:30
Optional< PetInfo > CurrentPet
Definition PetDefines.h:112
Definition Pet.h:40
Player * GetOwner() const
Definition Pet.cpp:1997
PetType getPetType() const
Definition Pet.h:51
void SendTalentsInfoData(bool pet)
Definition Player.cpp:25337
void SetGroupUpdateFlag(uint32 flag)
Definition Player.h:2165
void SendDirectMessage(WorldPacket const *data) const
Definition Player.cpp:6163
void CharmSpellInitialize()
Definition Player.cpp:20722
PetStable * GetPetStable()
Definition Player.h:1045
void PetSpellInitialize()
Definition Player.cpp:20594
Pet * GetPet() const
Definition Player.cpp:20284
void LearnPetTalent(ObjectGuid petGuid, uint32 talentId, uint32 talentRank)
Definition Player.cpp:25021
void StopCastingCharm()
Definition Player.cpp:20412
void RemovePet(Pet *pet, PetSaveMode mode, bool returnreagent=false)
Definition Player.cpp:20308
Group * GetGroup()
Definition Player.h:2159
void PossessSpellInitialize()
Definition Player.cpp:20643
void VehicleSpellInitialize()
Definition Player.cpp:20671
void setUInt32(uint8 index, uint32 value)
void setString(uint8 index, std::string const &value)
Unit * GetUnitTarget() const
Definition Spell.cpp:273
void ResetCooldown(uint32 spellId, bool update=false)
bool HasCooldown(SpellInfo const *spellInfo, uint32 itemId=0, bool ignoreCategoryCooldown=false) const
bool IsAutocastable() const
bool IsPassive() const
uint32 Id
Definition SpellInfo.h:289
bool HasAttribute(SpellAttr0 attribute) const
Definition SpellInfo.h:375
std::array< SpellEffectInfo, MAX_SPELL_EFFECTS > const & GetEffects() const
Definition SpellInfo.h:482
Definition Spell.h:152
bool m_fromClient
Definition Spell.h:401
SpellCastTargets m_targets
Definition Spell.h:409
void SendPetCastResult(SpellCastResult result)
Definition Spell.cpp:4177
uint8 m_cast_count
Definition Spell.h:400
SpellCastResult prepare(SpellCastTargets const &targets, AuraEffect const *triggeredByAura=nullptr)
Definition Spell.cpp:3091
SpellCastResult CheckPetCast(Unit *target)
Definition Spell.cpp:6166
void InitExplicitTargets(SpellCastTargets const &targets)
Definition Spell.cpp:675
void finish(bool ok=true)
Definition Spell.cpp:3943
static void SendCastResult(Player *caster, SpellInfo const *spellInfo, uint8 castCount, SpellCastResult result, SpellCustomErrors customError=SPELL_CUSTOM_ERROR_NONE, uint32 *param1=nullptr, uint32 *param2=nullptr)
Definition Spell.cpp:4150
virtual void UnSummon(uint32 msTime=0)
virtual void AttackStart(Unit *)
Definition UnitAI.cpp:30
Definition Unit.h:783
Unit * GetCharmed() const
Definition Unit.h:1269
bool IsVehicle() const
Definition Unit.h:901
bool HasPetFlag(UnitPetFlag flags) const
Definition Unit.h:1007
Pet * ToPet()
Definition Unit.h:1804
void SendPetAIReaction(ObjectGuid guid) const
Definition Unit.cpp:10265
ObjectGuid GetOwnerGUID() const override
Definition Unit.h:1254
void InterruptNonMeleeSpells(bool withDelayed, uint32 spellid=0, bool withInstant=true)
Definition Unit.cpp:3116
MotionMaster * GetMotionMaster()
Definition Unit.h:1683
bool IsPet() const
Definition Unit.h:898
void SetPetNameTimestamp(uint32 timestamp)
Definition Unit.h:1304
bool IsAlive() const
Definition Unit.h:1247
TempSummon * ToTempSummon()
Definition Unit.h:1810
CharmInfo * GetCharmInfo()
Definition Unit.h:1300
ControlList m_Controlled
Definition Unit.h:1289
virtual bool HasSpell(uint32) const
Definition Unit.h:1175
Unit * GetCharmerOrOwner() const
Definition Unit.h:1278
bool IsAIEnabled() const
Definition Unit.h:812
bool Attack(Unit *victim, bool meleeAttack)
Definition Unit.cpp:5477
bool isPossessed() const
Definition Unit.h:1295
bool IsSummon() const
Definition Unit.h:896
void SetPower(Powers power, uint32 val, bool withPowerUpdate=true, bool force=false)
Definition Unit.cpp:9398
void SendPetActionSound(PetAction action) const
Definition Unit.cpp:10245
void RemovePetFlag(UnitPetFlag flags)
Definition Unit.h:1009
ObjectGuid GetCritterGUID() const
Definition Unit.h:1262
virtual float GetFollowAngle() const
Definition Unit.h:1798
Unit * GetVictim() const
Definition Unit.h:873
Unit * GetFirstControlled() const
Definition Unit.cpp:6179
uint32 HasUnitTypeMask(uint32 mask) const
Definition Unit.h:894
SpellHistory * GetSpellHistory()
Definition Unit.h:1497
ObjectGuid GetCharmerGUID() const
Definition Unit.h:1265
virtual bool HasSpellFocus(Spell const *=nullptr) const
Definition Unit.h:1494
uint32 GetPower(Powers power) const
Definition Unit.h:948
bool HasAuraTypeWithTriggerSpell(AuraType auratype, uint32 triggerSpell) const
Definition Unit.cpp:4605
void SetInFront(WorldObject const *target)
Definition Unit.cpp:13220
Guardian * GetGuardianPet() const
Definition Unit.cpp:5836
bool AttackStop()
Definition Unit.cpp:5587
ObjectGuid GetPetGUID() const
Definition Unit.h:1260
void SendPetDismissSound() const
Definition Unit.cpp:10253
Unit * GetOwner() const
Definition Object.cpp:2181
std::string const & GetName() const
Definition Object.h:384
void SetName(std::string newname)
Definition Object.h:385
void HandlePetCastSpellOpcode(WorldPackets::Spells::PetCastSpell &petCastSpell)
void HandleMovementOpcode(OpcodeClient opcode, MovementInfo &movementInfo)
bool CheckStableMaster(ObjectGuid guid)
void SendPacket(WorldPacket const *packet)
Send a packet to the client.
void HandlePetRename(WorldPacket &recvData)
void HandleDismissCritter(WorldPackets::Pet::DismissCritter &dismissCritter)
void SendQueryPetNameResponse(ObjectGuid guid, uint32 petnumber)
void HandleQueryPetName(WorldPacket &recvData)
void HandlePetStopAttack(WorldPackets::Pet::PetStopAttack &packet)
void SendPetNameInvalid(uint32 error, std::string const &name, DeclinedName *declinedName)
LocaleConstant GetSessionDbcLocale() const
Player * GetPlayer() const
void HandlePetSetAction(WorldPacket &recvData)
void HandleLearnPreviewTalentsPet(WorldPacket &recvPacket)
void HandlePetAction(WorldPacket &recvData)
void HandlePetSpellAutocastOpcode(WorldPackets::Pet::PetSpellAutocast &packet)
uint32 GetAccountId() const
void HandlePetLearnTalent(WorldPacket &recvPacket)
void HandleRequestPetInfo(WorldPackets::Pet::RequestPetInfo &packet)
Player * _player
void HandlePetAbandon(WorldPackets::Pet::PetAbandon &packet)
void HandlePetActionHelper(Unit *pet, ObjectGuid guid1, uint32 spellid, uint16 flag, ObjectGuid guid2)
@ SMSG_PET_NAME_INVALID
Definition Opcodes.h:405
@ MSG_MOVE_STOP
Definition Opcodes.h:212
@ SMSG_PET_NAME_QUERY_RESPONSE
Definition Opcodes.h:112
time_t GetGameTime()
Definition GameTime.cpp:42
TC_GAME_API Unit * GetUnit(WorldObject const &, ObjectGuid const &guid)
TC_GAME_API Pet * GetPet(WorldObject const &, ObjectGuid const &guid)
TC_GAME_API Creature * GetCreatureOrPetOrVehicle(WorldObject const &, ObjectGuid const &)
void SaveStayPosition()
Definition Unit.cpp:13169
void SetIsCommandFollow(bool val)
Definition Unit.cpp:13159
bool IsCommandAttack()
Definition Unit.cpp:13154
uint32 GetPetNumber() const
Definition Unit.h:692
void SetActionBar(uint8 index, uint32 spellOrAction, ActiveStates type)
Definition Unit.h:710
void SetIsAtStay(bool val)
Definition Unit.cpp:13190
void SetIsFollowing(bool val)
Definition Unit.cpp:13200
void SetIsReturning(bool val)
Definition Unit.cpp:13210
UnitActionBarEntry const * GetActionBarEntry(uint8 index) const
Definition Unit.h:714
void SetSpellAutocast(SpellInfo const *spellInfo, bool state)
Definition Unit.cpp:9966
void ToggleCreatureAutocast(SpellInfo const *spellInfo, bool apply)
Definition Unit.cpp:9910
void SetCommandState(CommandStates st)
Definition Unit.h:695
void SetIsCommandAttack(bool val)
Definition Unit.cpp:13149
std::string name[MAX_DECLINED_NAME_CASES]
ActiveStates GetType() const
Definition Unit.h:640
uint32 GetAction() const
Definition Unit.h:641
Optional< MissileTrajectoryRequest > MissileTrajectory