TrinityCore
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 "CharmInfo.h"
20#include "Common.h"
21#include "CreatureAI.h"
22#include "DatabaseEnv.h"
23#include "Group.h"
24#include "Log.h"
25#include "Map.h"
26#include "MotionMaster.h"
27#include "ObjectAccessor.h"
28#include "ObjectMgr.h"
29#include "Pet.h"
30#include "PetPackets.h"
31#include "Player.h"
32#include "QueryPackets.h"
33#include "Spell.h"
34#include "SpellHistory.h"
35#include "SpellInfo.h"
36#include "SpellMgr.h"
37#include "SpellPackets.h"
38#include "PetAI.h"
39#include "Util.h"
40
42{
44
45 if (!pet)
46 {
47 TC_LOG_DEBUG("entities.pet", "Critter ({}) 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 {
57 _player->SetBattlePetData(nullptr);
58
59 pet->ToTempSummon()->UnSummon();
60 }
61 }
62}
63
65{
66 if (_player->IsMounted())
67 return;
68
69 ObjectGuid guid1 = packet.PetGUID; //pet guid
70 ObjectGuid guid2 = packet.TargetGUID; //tag guid
71
72 uint32 spellid = UNIT_ACTION_BUTTON_ACTION(packet.Action);
73 uint8 flag = UNIT_ACTION_BUTTON_TYPE(packet.Action); //delete = 0x07 CastSpell = C1
74
75 // used also for charmed creature
76 Unit* pet = ObjectAccessor::GetUnit(*_player, guid1);
77 TC_LOG_DEBUG("entities.pet", "HandlePetAction: {} - flag: {}, spellid: {}, target: {}.", guid1.ToString(), uint32(flag), spellid, guid2.ToString());
78
79 if (!pet)
80 {
81 TC_LOG_DEBUG("entities.pet", "HandlePetAction: {} doesn't exist for {} {}", guid1.ToString(), GetPlayer()->GetGUID().ToString(), GetPlayer()->GetName());
82 return;
83 }
84
85 if (pet != GetPlayer()->GetFirstControlled())
86 {
87 TC_LOG_DEBUG("entities.pet", "HandlePetAction: {} does not belong to {} {}", guid1.ToString(), GetPlayer()->GetGUID().ToString(), GetPlayer()->GetName());
88 return;
89 }
90
91 if (!pet->IsAlive())
92 {
93 SpellInfo const* spell = (flag == ACT_ENABLED || flag == ACT_PASSIVE) ? sSpellMgr->GetSpellInfo(spellid, pet->GetMap()->GetDifficultyID()) : nullptr;
94 if (!spell)
95 return;
97 return;
98 }
99
101 if (pet->GetTypeId() == TYPEID_PLAYER && !(flag == ACT_COMMAND && spellid == COMMAND_ATTACK))
102 return;
103
104 if (GetPlayer()->m_Controlled.size() == 1)
105 HandlePetActionHelper(pet, guid1, spellid, flag, guid2, packet.ActionPosition);
106 else
107 {
108 // If a pet is dismissed, m_Controlled will change
109 std::vector<Unit*> controlled;
110 for (Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr)
111 if ((*itr)->GetEntry() == pet->GetEntry() && (*itr)->IsAlive())
112 controlled.push_back(*itr);
113 for (std::vector<Unit*>::iterator itr = controlled.begin(); itr != controlled.end(); ++itr)
114 HandlePetActionHelper(*itr, guid1, spellid, flag, guid2, packet.ActionPosition);
115 }
116}
117
119{
121
122 if (!pet)
123 {
124 TC_LOG_ERROR("entities.pet", "HandlePetStopAttack: {} does not exist", packet.PetGUID.ToString());
125 return;
126 }
127
128 if (pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharmed())
129 {
130 TC_LOG_ERROR("entities.pet", "HandlePetStopAttack: {} isn't a pet or charmed creature of player {}",
131 packet.PetGUID.ToString(), GetPlayer()->GetName());
132 return;
133 }
134
135 if (!pet->IsAlive())
136 return;
137
138 pet->AttackStop();
139}
140
141void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spellid, uint16 flag, ObjectGuid guid2, Position const& pos)
142{
143 CharmInfo* charmInfo = pet->GetCharmInfo();
144 if (!charmInfo)
145 {
146 TC_LOG_DEBUG("entities.pet", "WorldSession::HandlePetAction(petGuid: {}, tagGuid: {}, spellId: {}, flag: {}): object {} is considered pet-like but doesn't have a charminfo!",
147 guid1.ToString(), guid2.ToString(), spellid, flag, pet->GetGUID().ToString());
148 return;
149 }
150
151 switch (flag)
152 {
153 case ACT_COMMAND: // 0x07
154 switch (spellid)
155 {
156 case COMMAND_STAY: // flat = 1792 - STAY
158 pet->GetMotionMaster()->MoveIdle();
159
160 charmInfo->SetCommandState(COMMAND_STAY);
161 charmInfo->SetIsCommandAttack(false);
162 charmInfo->SetIsAtStay(true);
163 charmInfo->SetIsCommandFollow(false);
164 charmInfo->SetIsFollowing(false);
165 charmInfo->SetIsReturning(false);
166 charmInfo->SaveStayPosition();
167 break;
168 case COMMAND_FOLLOW: // spellid = 1792 - FOLLOW
169 pet->AttackStop();
170 pet->InterruptNonMeleeSpells(false);
172
174 charmInfo->SetIsCommandAttack(false);
175 charmInfo->SetIsAtStay(false);
176 charmInfo->SetIsReturning(true);
177 charmInfo->SetIsCommandFollow(true);
178 charmInfo->SetIsFollowing(false);
179 break;
180 case COMMAND_ATTACK: // spellid = 1792 - ATTACK
181 {
182 // Can't attack if owner is pacified
184 {
185 // pet->SendPetCastFail(spellid, SPELL_FAILED_PACIFIED);
187 return;
188 }
189
190 // only place where pet can be player
191 Unit* TargetUnit = ObjectAccessor::GetUnit(*_player, guid2);
192 if (!TargetUnit)
193 return;
194
195 if (Unit* owner = pet->GetOwner())
196 if (!owner->IsValidAttackTarget(TargetUnit))
197 return;
198
199 // This is true if pet has no target or has target but targets differs.
200 if (pet->GetVictim() != TargetUnit || !pet->GetCharmInfo()->IsCommandAttack())
201 {
202 if (pet->GetVictim())
203 pet->AttackStop();
204
205 if (pet->GetTypeId() != TYPEID_PLAYER && pet->ToCreature()->IsAIEnabled())
206 {
207 charmInfo->SetIsCommandAttack(true);
208 charmInfo->SetIsAtStay(false);
209 charmInfo->SetIsFollowing(false);
210 charmInfo->SetIsCommandFollow(false);
211 charmInfo->SetIsReturning(false);
212
213 CreatureAI* AI = pet->ToCreature()->AI();
214 if (PetAI* petAI = dynamic_cast<PetAI*>(AI))
215 petAI->_AttackStart(TargetUnit); // force target switch
216 else
217 AI->AttackStart(TargetUnit);
218
219 // 10% chance to play special pet attack talk, else growl
220 if (pet->IsPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != TargetUnit && urand(0, 100) < 10)
222 else
223 {
224 // 90% chance for pet and 100% chance for charmed creature
225 pet->SendPetAIReaction(guid1);
226 }
227 }
228 else // charmed player
229 {
230 charmInfo->SetIsCommandAttack(true);
231 charmInfo->SetIsAtStay(false);
232 charmInfo->SetIsFollowing(false);
233 charmInfo->SetIsCommandFollow(false);
234 charmInfo->SetIsReturning(false);
235
236 pet->Attack(TargetUnit, true);
237 pet->SendPetAIReaction(guid1);
238 }
239 }
240 break;
241 }
242 case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet)
243 if (pet->GetCharmerGUID() == GetPlayer()->GetGUID())
245 else if (pet->GetOwnerGUID() == GetPlayer()->GetGUID())
246 {
247 ASSERT(pet->GetTypeId() == TYPEID_UNIT);
248 if (pet->IsPet())
249 {
250 if (((Pet*)pet)->getPetType() == HUNTER_PET)
252 else
254 }
255 else if (pet->HasUnitTypeMask(UNIT_MASK_MINION))
256 {
257 ((Minion*)pet)->UnSummon();
258 }
259 }
260 break;
261 case COMMAND_MOVE_TO:
262 pet->StopMoving();
263 pet->GetMotionMaster()->Clear();
264 pet->GetMotionMaster()->MovePoint(0, pos);
266
267 charmInfo->SetIsCommandAttack(false);
268 charmInfo->SetIsAtStay(true);
269 charmInfo->SetIsFollowing(false);
270 charmInfo->SetIsReturning(false);
271 charmInfo->SaveStayPosition();
272 break;
273 default:
274 TC_LOG_ERROR("entities.pet", "WORLD: unknown PET flag Action {} and spellid {}.", uint32(flag), spellid);
275 }
276 break;
277 case ACT_REACTION: // 0x6
278 switch (spellid)
279 {
280 case REACT_PASSIVE: // passive
281 pet->AttackStop();
282 [[fallthrough]];
283 case REACT_DEFENSIVE: // recovery
284 case REACT_AGGRESSIVE: // activete
285 if (pet->GetTypeId() == TYPEID_UNIT)
286 pet->ToCreature()->SetReactState(ReactStates(spellid));
287 break;
288 }
289 break;
290 case ACT_DISABLED: // 0x81 spell (disabled), ignore
291 case ACT_PASSIVE: // 0x01
292 case ACT_ENABLED: // 0xC1 spell
293 {
294 Unit* unit_target = nullptr;
295
296 if (!guid2.IsEmpty())
297 unit_target = ObjectAccessor::GetUnit(*_player, guid2);
298
299 // do not cast unknown spells
300 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid, pet->GetMap()->GetDifficultyID());
301 if (!spellInfo)
302 {
303 TC_LOG_ERROR("spells.pet", "WORLD: unknown PET spell id {}", spellid);
304 return;
305 }
306
307 for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects())
308 {
309 if (spellEffectInfo.TargetA.GetTarget() == TARGET_UNIT_SRC_AREA_ENEMY || spellEffectInfo.TargetA.GetTarget() == TARGET_UNIT_DEST_AREA_ENEMY || spellEffectInfo.TargetA.GetTarget() == TARGET_DEST_DYNOBJ_ENEMY)
310 return;
311 }
312
313 // do not cast not learned spells
314 if (!pet->HasSpell(spellid) || spellInfo->IsPassive())
315 return;
316
317 // Clear the flags as if owner clicked 'attack'. AI will reset them
318 // after AttackStart, even if spell failed
319 if (pet->GetCharmInfo())
320 {
321 pet->GetCharmInfo()->SetIsAtStay(false);
322 pet->GetCharmInfo()->SetIsCommandAttack(true);
323 pet->GetCharmInfo()->SetIsReturning(false);
324 pet->GetCharmInfo()->SetIsFollowing(false);
325 }
326
327 Spell* spell = new Spell(pet, spellInfo, TRIGGERED_NONE);
328
329 SpellCastResult result = spell->CheckPetCast(unit_target);
330
331 // auto turn to target unless possessed
332 if (result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->isPossessed() && !pet->IsVehicle())
333 {
334 if (unit_target)
335 {
336 if (!pet->HasSpellFocus())
337 pet->SetInFront(unit_target);
338 if (Player* player = unit_target->ToPlayer())
339 pet->SendUpdateToPlayer(player);
340 }
341 else if (Unit* unit_target2 = spell->m_targets.GetUnitTarget())
342 {
343 if (!pet->HasSpellFocus())
344 pet->SetInFront(unit_target2);
345 if (Player* player = unit_target2->ToPlayer())
346 pet->SendUpdateToPlayer(player);
347 }
348
349 if (Unit* powner = pet->GetCharmerOrOwner())
350 if (Player* player = powner->ToPlayer())
351 pet->SendUpdateToPlayer(player);
352
353 result = SPELL_CAST_OK;
354 }
355
356 if (result == SPELL_CAST_OK)
357 {
358 unit_target = spell->m_targets.GetUnitTarget();
359
360 // 10% chance to play special pet attack talk, else growl
361 // actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
362 if (pet->IsPet() && (((Pet*)pet)->getPetType() == SUMMON_PET) && (pet != unit_target) && (urand(0, 100) < 10))
364 else
365 {
366 pet->SendPetAIReaction(guid1);
367 }
368
369 if (unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->isPossessed() && !pet->IsVehicle())
370 {
371 // This is true if pet has no target or has target but targets differs.
372 if (pet->GetVictim() != unit_target)
373 {
374 if (CreatureAI* AI = pet->ToCreature()->AI())
375 {
376 if (PetAI* petAI = dynamic_cast<PetAI*>(AI))
377 petAI->_AttackStart(unit_target); // force victim switch
378 else
379 AI->AttackStart(unit_target);
380 }
381 }
382 }
383
384 spell->prepare(spell->m_targets);
385 }
386 else
387 {
388 if (pet->isPossessed() || pet->IsVehicle())
389 Spell::SendCastResult(GetPlayer(), spellInfo, spell->m_SpellVisual, spell->m_castId, result);
390 else
391 spell->SendPetCastResult(result);
392
393 if (!pet->GetSpellHistory()->HasCooldown(spellid))
394 pet->GetSpellHistory()->ResetCooldown(spellid, true);
395
396 spell->finish(result);
397 delete spell;
398
399 // reset specific flags in case of spell fail. AI will reset other flags
400 if (pet->GetCharmInfo())
401 pet->GetCharmInfo()->SetIsCommandAttack(false);
402 }
403 break;
404 }
405 default:
406 TC_LOG_ERROR("entities.pet", "WORLD: unknown PET flag Action {} and spellid {}.", uint32(flag), spellid);
407 }
408}
409
411{
413}
414
416{
418
419 response.UnitGUID = guid;
420
422 {
423 response.Allow = true;
424 response.Timestamp = *unit->m_unitData->PetNameTimestamp;
425 response.Name = unit->GetName();
426
427 if (Pet* pet = unit->ToPet())
428 {
429 if (DeclinedName const* names = pet->GetDeclinedNames())
430 {
431 response.HasDeclined = true;
432 response.DeclinedNames = *names;
433 }
434 }
435 }
436
437 _player->SendDirectMessage(response.Write());
438}
439
441{
442 // spell case or GM
443 if (guid == GetPlayer()->GetGUID())
444 {
445 if (!GetPlayer()->IsGameMaster() && !GetPlayer()->HasAuraType(SPELL_AURA_OPEN_STABLE))
446 {
447 TC_LOG_DEBUG("entities.player.cheat", "{} attempt open stable in cheating way.", guid.ToString());
448 return false;
449 }
450 }
451 // stable master case
452 else
453 {
454 if (!GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_STABLEMASTER, UNIT_NPC_FLAG_2_NONE))
455 {
456 TC_LOG_DEBUG("entities.player", "Stablemaster {} not found or you can't interact with him.", guid.ToString());
457 return false;
458 }
459 }
460 return true;
461}
462
464{
465 ObjectGuid petguid = packet.PetGUID;
466 Unit* pet = ObjectAccessor::GetUnit(*_player, petguid);
467
468 if (!pet || pet != _player->GetFirstControlled())
469 {
470 TC_LOG_ERROR("entities.pet", "HandlePetSetAction: Unknown {} or owner ({})", petguid.ToString(), _player->GetGUID().ToString());
471 return;
472 }
473
474 CharmInfo* charmInfo = pet->GetCharmInfo();
475 if (!charmInfo)
476 {
477 TC_LOG_ERROR("entities.pet", "WorldSession::HandlePetSetAction: object {} is considered pet-like but doesn't have a charminfo!", pet->GetGUID().ToString());
478 return;
479 }
480
481 std::vector<Unit*> pets;
482 for (Unit* controlled : _player->m_Controlled)
483 if (controlled->GetEntry() == pet->GetEntry() && controlled->IsAlive())
484 pets.push_back(controlled);
485
486 uint32 position = packet.Index;
487 uint32 actionData = packet.Action;
488
489 uint32 spell_id = UNIT_ACTION_BUTTON_ACTION(actionData);
490 uint8 act_state = UNIT_ACTION_BUTTON_TYPE(actionData);
491
492 TC_LOG_DEBUG("entities.pet", "Player {} has changed pet spell action. Position: {}, Spell: {}, State: 0x{:X}",
493 _player->GetName(), position, spell_id, uint32(act_state));
494
495 for (Unit* petControlled : pets)
496 {
497 //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
498 if (!((act_state == ACT_ENABLED || act_state == ACT_DISABLED || act_state == ACT_PASSIVE) && spell_id && !petControlled->HasSpell(spell_id)))
499 {
500 if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id, petControlled->GetMap()->GetDifficultyID()))
501 {
502 //sign for autocast
503 if (act_state == ACT_ENABLED)
504 {
505 if (petControlled->GetTypeId() == TYPEID_UNIT && petControlled->IsPet())
506 ((Pet*)petControlled)->ToggleAutocast(spellInfo, true);
507 else
508 for (Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr)
509 if ((*itr)->GetEntry() == petControlled->GetEntry())
510 (*itr)->GetCharmInfo()->ToggleCreatureAutocast(spellInfo, true);
511 }
512 //sign for no/turn off autocast
513 else if (act_state == ACT_DISABLED)
514 {
515 if (petControlled->GetTypeId() == TYPEID_UNIT && petControlled->IsPet())
516 ((Pet*)petControlled)->ToggleAutocast(spellInfo, false);
517 else
518 for (Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr)
519 if ((*itr)->GetEntry() == petControlled->GetEntry())
520 (*itr)->GetCharmInfo()->ToggleCreatureAutocast(spellInfo, false);
521 }
522 }
523
524 charmInfo->SetActionBar(position, spell_id, ActiveStates(act_state));
525 }
526 }
527}
528
530{
531 ObjectGuid petguid = packet.RenameData.PetGUID;
532
533 std::string name = packet.RenameData.NewName;
534 Optional<DeclinedName> const& declinedname = packet.RenameData.DeclinedNames;
535
536 PetStable* petStable = _player->GetPetStable();
537 Pet* pet = ObjectAccessor::GetPet(*_player, petguid);
538 // check it!
539 if (!pet || !pet->IsPet() || ((Pet*)pet)->getPetType() != HUNTER_PET ||
541 pet->GetOwnerGUID() != _player->GetGUID() || !pet->GetCharmInfo() ||
542 !petStable || !petStable->GetCurrentPet() || petStable->GetCurrentPet()->PetNumber != pet->GetCharmInfo()->GetPetNumber())
543 return;
544
546 if (res != PET_NAME_SUCCESS)
547 {
548 SendPetNameInvalid(res, name, {});
549 return;
550 }
551
552 if (sObjectMgr->IsReservedName(name))
553 {
555 return;
556 }
557
558 pet->SetName(name);
559
561
563
564 petStable->GetCurrentPet()->Name = name;
565 petStable->GetCurrentPet()->WasRenamed = true;
566
567 if (declinedname)
568 {
569 std::wstring wname;
570 if (!Utf8toWStr(name, wname))
571 return;
572
573 if (!ObjectMgr::CheckDeclinedNames(wname, *declinedname))
574 {
576 return;
577 }
578 }
579
580 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
581 if (declinedname)
582 {
584 stmt->setUInt32(0, pet->GetCharmInfo()->GetPetNumber());
585 trans->Append(stmt);
586
587 stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_PET_DECLINEDNAME);
588 stmt->setUInt32(0, pet->GetCharmInfo()->GetPetNumber());
589 stmt->setUInt64(1, _player->GetGUID().GetCounter());
590
591 for (uint8 i = 0; i < 5; i++)
592 stmt->setString(i + 2, declinedname->name[i]);
593
594 trans->Append(stmt);
595 }
596
598 stmt->setString(0, name);
599 stmt->setUInt64(1, _player->GetGUID().GetCounter());
600 stmt->setUInt32(2, pet->GetCharmInfo()->GetPetNumber());
601 trans->Append(stmt);
602
603 CharacterDatabase.CommitTransaction(trans);
604
606}
607
609{
610 // pet/charmed
612 if (pet && pet->ToPet() && pet->ToPet()->getPetType() == HUNTER_PET)
613 {
615 }
616}
617
619{
621 if (!pet)
622 {
623 TC_LOG_ERROR("entities.pet", "WorldSession::HandlePetSpellAutocastOpcode: Pet {} not found.", packet.PetGUID.ToString());
624 return;
625 }
626
627 if (pet != _player->GetGuardianPet() && pet != _player->GetCharmed())
628 {
629 TC_LOG_ERROR("entities.pet", "WorldSession::HandlePetSpellAutocastOpcode: {} isn't pet of player {} ({}).",
630 packet.PetGUID.ToString(), GetPlayer()->GetName(), GetPlayer()->GetGUID().ToString());
631 return;
632 }
633
634 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(packet.SpellID, pet->GetMap()->GetDifficultyID());
635 if (!spellInfo)
636 {
637 TC_LOG_ERROR("spells.pet", "WorldSession::HandlePetSpellAutocastOpcode: Unknown spell id {} used by {}.", packet.SpellID, packet.PetGUID.ToString());
638 return;
639 }
640
641 std::vector<Unit*> pets;
642 for (Unit* controlled : _player->m_Controlled)
643 if (controlled->GetEntry() == pet->GetEntry() && controlled->IsAlive())
644 pets.push_back(controlled);
645
646 for (Unit* petControlled : pets)
647 {
648 // do not add not learned spells/ passive spells
649 if (!petControlled->HasSpell(packet.SpellID) || !spellInfo->IsAutocastable())
650 return;
651
652 CharmInfo* charmInfo = petControlled->GetCharmInfo();
653 if (!charmInfo)
654 {
655 TC_LOG_ERROR("entities.pet", "WorldSession::HandlePetSpellAutocastOpcode: object {} is considered pet-like but doesn't have a charminfo!", petControlled->GetGUID().ToString());
656 return;
657 }
658
659 if (petControlled->IsPet())
660 petControlled->ToPet()->ToggleAutocast(spellInfo, packet.AutocastEnabled);
661 else
662 charmInfo->ToggleCreatureAutocast(spellInfo, packet.AutocastEnabled);
663
664 charmInfo->SetSpellAutocast(spellInfo, packet.AutocastEnabled);
665 }
666}
667
669{
670 Unit* caster = ObjectAccessor::GetUnit(*_player, petCastSpell.PetGUID);
671 if (!caster)
672 {
673 TC_LOG_ERROR("entities.pet", "WorldSession::HandlePetCastSpellOpcode: Caster {} not found.", petCastSpell.PetGUID.ToString());
674 return;
675 }
676
677 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(petCastSpell.Cast.SpellID, caster->GetMap()->GetDifficultyID());
678 if (!spellInfo)
679 {
680 TC_LOG_ERROR("spells.pet", "WorldSession::HandlePetCastSpellOpcode: unknown spell id {} tried to cast by {}",
681 petCastSpell.Cast.SpellID, petCastSpell.PetGUID.ToString());
682 return;
683 }
684
685 // This opcode is also sent from charmed and possessed units (players and creatures)
686 if (caster != _player->GetGuardianPet() && caster != _player->GetCharmed())
687 {
688 TC_LOG_ERROR("spells.pet", "WorldSession::HandlePetCastSpellOpcode: {} isn't pet of player {} ({}).", petCastSpell.PetGUID.ToString(), GetPlayer()->GetName(), GetPlayer()->GetGUID().ToString());
689 return;
690 }
691
692 SpellCastTargets targets(caster, petCastSpell.Cast);
693
694 TriggerCastFlags triggerCastFlags = TRIGGERED_NONE;
695
696 if (spellInfo->IsPassive())
697 return;
698
699 // cast only learned spells
700 if (!caster->HasSpell(spellInfo->Id))
701 {
702 bool allow = false;
703
704 // allow casting of spells triggered by clientside periodic trigger auras
706 {
707 allow = true;
708 triggerCastFlags = TRIGGERED_FULL_MASK;
709 }
710
711 if (!allow)
712 return;
713 }
714
715 Spell* spell = new Spell(caster, spellInfo, triggerCastFlags);
716 spell->m_fromClient = true;
717 spell->m_misc.Raw.Data[0] = petCastSpell.Cast.Misc[0];
718 spell->m_misc.Raw.Data[1] = petCastSpell.Cast.Misc[1];
719 spell->m_targets = targets;
720
721 SpellCastResult result = spell->CheckPetCast(nullptr);
722
723 if (result == SPELL_CAST_OK)
724 {
725 if (Creature* creature = caster->ToCreature())
726 {
727 if (Pet* pet = creature->ToPet())
728 {
729 // 10% chance to play special pet attack talk, else growl
730 // actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
731 if (pet->getPetType() == SUMMON_PET && (urand(0, 100) < 10))
732 pet->SendPetTalk(PET_TALK_SPECIAL_SPELL);
733 else
734 pet->SendPetAIReaction(petCastSpell.PetGUID);
735 }
736 }
737
739 spellPrepare.ClientCastID = petCastSpell.Cast.CastID;
740 spellPrepare.ServerCastID = spell->m_castId;
741 SendPacket(spellPrepare.Write());
742
743 spell->prepare(targets);
744 }
745 else
746 {
747 spell->SendPetCastResult(result);
748
749 if (!caster->GetSpellHistory()->HasCooldown(spellInfo))
750 caster->GetSpellHistory()->ResetCooldown(spellInfo->Id, true);
751
752 spell->finish(result);
753 delete spell;
754 }
755}
756
757void WorldSession::SendPetNameInvalid(uint32 error, const std::string& name, Optional<DeclinedName> const& declinedName)
758{
760 petNameInvalid.Result = error;
761 petNameInvalid.RenameData.NewName = name;
762 petNameInvalid.RenameData.DeclinedNames = declinedName;
763
764 SendPacket(petNameInvalid.Write());
765}
766
768{
769 // Handle the packet CMSG_REQUEST_PET_INFO - sent when player does ingame /reload command
770
771 // Packet sent when player has a pet
772 if (_player->GetPet())
774 else if (Unit* charm = _player->GetCharmed())
775 {
776 // Packet sent when player has a possessed unit
777 if (charm->HasUnitState(UNIT_STATE_POSSESSED))
779 // Packet sent when player controlling a vehicle
780 else if (charm->HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED) && charm->HasUnitFlag(UNIT_FLAG_POSSESSED))
782 // Packet sent when player has a charmed unit
783 else
785 }
786}
@ CHAR_UPD_CHAR_PET_NAME
@ CHAR_INS_CHAR_PET_DECLINEDNAME
@ CHAR_DEL_CHAR_PET_DECLINEDNAME
#define UNIT_ACTION_BUTTON_ACTION(X)
Definition: CharmInfo.h:33
#define UNIT_ACTION_BUTTON_TYPE(X)
Definition: CharmInfo.h:34
SQLTransaction< CharacterDatabaseConnection > CharacterDatabaseTransaction
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
Definition: DatabaseEnv.cpp:21
uint8_t uint8
Definition: Define.h:144
uint16_t uint16
Definition: Define.h:143
uint32_t uint32
Definition: Define.h:142
#define ASSERT
Definition: Errors.h:68
@ GROUP_UPDATE_FLAG_PET_NAME
Definition: Group.h:153
#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:40
@ TYPEID_PLAYER
Definition: ObjectGuid.h:41
#define sObjectMgr
Definition: ObjectMgr.h:1946
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition: Optional.h:25
#define PET_FOLLOW_DIST
Definition: PetDefines.h:97
@ SUMMON_PET
Definition: PetDefines.h:31
@ HUNTER_PET
Definition: PetDefines.h:32
@ PET_TALK_SPECIAL_SPELL
Definition: PetDefines.h:93
@ PET_TALK_ATTACK
Definition: PetDefines.h:94
@ PET_SAVE_AS_DELETED
Definition: PetDefines.h:42
@ PET_SAVE_NOT_IN_SLOT
Definition: PetDefines.h:48
uint32 urand(uint32 min, uint32 max)
Definition: Random.cpp:42
@ TARGET_DEST_DYNOBJ_ENEMY
@ TARGET_UNIT_DEST_AREA_ENEMY
@ TARGET_UNIT_SRC_AREA_ENEMY
@ SPELL_ATTR0_ALLOW_CAST_WHILE_DEAD
SpellCastResult
@ SPELL_FAILED_UNIT_NOT_INFRONT
@ SPELL_CAST_OK
PetNameInvalidReason
@ PET_NAME_RESERVED
@ PET_NAME_SUCCESS
@ PET_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME
@ SPELL_AURA_MOD_PACIFY
@ SPELL_AURA_OPEN_STABLE
@ SPELL_AURA_PERIODIC_TRIGGER_SPELL_FROM_CLIENT
TriggerCastFlags
Definition: SpellDefines.h:245
@ TRIGGERED_FULL_MASK
Used when doing CastSpell with triggered == true.
Definition: SpellDefines.h:266
@ TRIGGERED_NONE
Not triggered.
Definition: SpellDefines.h:246
#define sSpellMgr
Definition: SpellMgr.h:849
@ UNIT_PET_FLAG_CAN_BE_RENAMED
Definition: UnitDefines.h:109
ReactStates
Definition: UnitDefines.h:505
@ REACT_DEFENSIVE
Definition: UnitDefines.h:507
@ REACT_PASSIVE
Definition: UnitDefines.h:506
@ REACT_AGGRESSIVE
Definition: UnitDefines.h:508
ActiveStates
Definition: UnitDefines.h:495
@ ACT_REACTION
Definition: UnitDefines.h:500
@ ACT_COMMAND
Definition: UnitDefines.h:499
@ ACT_ENABLED
Definition: UnitDefines.h:498
@ ACT_PASSIVE
Definition: UnitDefines.h:496
@ ACT_DISABLED
Definition: UnitDefines.h:497
@ UNIT_NPC_FLAG_STABLEMASTER
Definition: UnitDefines.h:319
@ UNIT_NPC_FLAG_2_NONE
Definition: UnitDefines.h:336
@ COMMAND_ATTACK
Definition: UnitDefines.h:528
@ COMMAND_MOVE_TO
Definition: UnitDefines.h:530
@ COMMAND_ABANDON
Definition: UnitDefines.h:529
@ COMMAND_STAY
Definition: UnitDefines.h:526
@ COMMAND_FOLLOW
Definition: UnitDefines.h:527
@ UNIT_FLAG_POSSESSED
Definition: UnitDefines.h:168
@ UNIT_FLAG_PLAYER_CONTROLLED
Definition: UnitDefines.h:147
@ UNIT_MASK_MINION
Definition: Unit.h:351
@ UNIT_STATE_POSSESSED
Definition: Unit.h:271
bool Utf8toWStr(char const *utf8str, size_t csize, wchar_t *wstr, size_t &wsize)
Definition: Util.cpp:383
void AttackStart(Unit *victim) override
== Triggered Actions Requested ==================
Definition: CreatureAI.cpp:328
void SetReactState(ReactStates st)
Definition: Creature.h:160
CreatureAI * AI() const
Definition: Creature.h:214
Difficulty GetDifficultyID() const
Definition: Map.h:324
void MovePoint(uint32 id, Position const &pos, bool generatePath=true, Optional< float > finalOrient={}, Optional< float > speed={}, MovementWalkRunSpeedSelectionMode speedSelectionMode=MovementWalkRunSpeedSelectionMode::Default, Optional< float > closeEnoughDistance={})
void MoveFollow(Unit *target, float dist, ChaseAngle angle, Optional< Milliseconds > duration={}, MovementSlot slot=MOTION_SLOT_ACTIVE)
LowType GetCounter() const
Definition: ObjectGuid.h:293
bool IsEmpty() const
Definition: ObjectGuid.h:319
std::string ToString() const
Definition: ObjectGuid.cpp:554
static bool CheckDeclinedNames(const std::wstring &w_ownname, DeclinedName const &names)
Definition: ObjectMgr.cpp:8921
static PetNameInvalidReason CheckPetName(std::string_view name)
Definition: ObjectMgr.cpp:8730
static Creature * ToCreature(Object *o)
Definition: Object.h:219
TypeID GetTypeId() const
Definition: Object.h:173
uint32 GetEntry() const
Definition: Object.h:161
static ObjectGuid GetGUID(Object const *o)
Definition: Object.h:159
void SendUpdateToPlayer(Player *player)
Definition: Object.cpp:183
static Player * ToPlayer(Object *o)
Definition: Object.h:213
Definition: PetAI.h:30
PetInfo * GetCurrentPet()
Definition: PetDefines.h:162
Definition: Pet.h:40
void SetGroupUpdateFlag(uint32 flag)
Definition: Pet.cpp:1828
PetType getPetType() const
Definition: Pet.h:51
ObjectGuid GetSummonedBattlePetGUID() const
Definition: Player.h:2820
void SendDirectMessage(WorldPacket const *data) const
Definition: Player.cpp:6324
void CharmSpellInitialize()
Definition: Player.cpp:21991
PetStable * GetPetStable()
Definition: Player.h:1222
void PetSpellInitialize()
Definition: Player.cpp:21879
Pet * GetPet() const
Definition: Player.cpp:21513
void StopCastingCharm()
Definition: Player.cpp:21677
void RemovePet(Pet *pet, PetSaveMode mode, bool returnreagent=false)
Definition: Player.cpp:21537
void SetBattlePetData(BattlePets::BattlePet const *pet=nullptr)
Definition: Player.cpp:21659
void PossessSpellInitialize()
Definition: Player.cpp:21918
void VehicleSpellInitialize()
Definition: Player.cpp:21944
void setUInt32(const uint8 index, const uint32 value)
void setString(const uint8 index, const std::string &value)
void setUInt64(const uint8 index, const uint64 value)
Unit * GetUnitTarget() const
Definition: Spell.cpp:218
void ResetCooldown(uint32 spellId, bool update=false)
bool HasCooldown(SpellInfo const *spellInfo, uint32 itemId=0) const
bool IsAutocastable() const
Definition: SpellInfo.cpp:1597
uint32 const Id
Definition: SpellInfo.h:325
bool IsPassive() const
Definition: SpellInfo.cpp:1592
bool HasAttribute(SpellAttr0 attribute) const
Definition: SpellInfo.h:449
std::vector< SpellEffectInfo > const & GetEffects() const
Definition: SpellInfo.h:576
Definition: Spell.h:255
bool m_fromClient
Definition: Spell.h:570
SpellCastTargets m_targets
Definition: Spell.h:607
struct Spell::@333::@335 Raw
static void SendCastResult(Player *caster, SpellInfo const *spellInfo, SpellCastVisual spellVisual, ObjectGuid cast_count, SpellCastResult result, SpellCustomErrors customError=SPELL_CUSTOM_ERROR_NONE, int32 *param1=nullptr, int32 *param2=nullptr)
Definition: Spell.cpp:4643
uint32 Data[2]
Definition: Spell.h:602
SpellCastResult prepare(SpellCastTargets const &targets, AuraEffect const *triggeredByAura=nullptr)
Definition: Spell.cpp:3426
SpellCastResult CheckPetCast(Unit *target)
Definition: Spell.cpp:6787
void SendPetCastResult(SpellCastResult result, int32 *param1=nullptr, int32 *param2=nullptr) const
Definition: Spell.cpp:4626
union Spell::@333 m_misc
void finish(SpellCastResult result=SPELL_CAST_OK)
Definition: Spell.cpp:4311
SpellCastVisual m_SpellVisual
Definition: Spell.h:606
ObjectGuid m_castId
Definition: Spell.h:568
virtual void UnSummon(uint32 msTime=0)
Definition: Unit.h:627
Unit * GetCharmed() const
Definition: Unit.h:1191
bool IsVehicle() const
Definition: Unit.h:743
bool HasPetFlag(UnitPetFlag flags) const
Definition: Unit.h:878
Pet * ToPet()
Definition: Unit.h:1750
ObjectGuid GetOwnerGUID() const override
Definition: Unit.h:1170
void InterruptNonMeleeSpells(bool withDelayed, uint32 spellid=0, bool withInstant=true)
Definition: Unit.cpp:3089
MotionMaster * GetMotionMaster()
Definition: Unit.h:1652
bool IsPet() const
Definition: Unit.h:740
void SetPetNameTimestamp(uint32 timestamp)
Definition: Unit.h:1225
bool IsAlive() const
Definition: Unit.h:1164
void StopMoving()
Definition: Unit.cpp:10049
TempSummon * ToTempSummon()
Definition: Unit.h:1756
CharmInfo * GetCharmInfo()
Definition: Unit.h:1221
ControlList m_Controlled
Definition: Unit.h:1211
virtual bool HasSpell(uint32) const
Definition: Unit.h:1069
Unit * GetCharmerOrOwner() const
Definition: Unit.h:1200
bool IsAIEnabled() const
Definition: Unit.h:658
bool Attack(Unit *victim, bool meleeAttack)
Definition: Unit.cpp:5670
bool isPossessed() const
Definition: Unit.h:1216
bool IsSummon() const
Definition: Unit.h:738
void SendPetTalk(uint32 pettalk)
Definition: Unit.cpp:10013
bool HasAuraType(AuraType auraType) const
Definition: Unit.cpp:4674
void RemovePetFlag(UnitPetFlag flags)
Definition: Unit.h:880
ObjectGuid GetCritterGUID() const
Definition: Unit.h:1178
bool IsMounted() const
Definition: Unit.h:898
void SendPetAIReaction(ObjectGuid guid)
Definition: Unit.cpp:10025
virtual float GetFollowAngle() const
Definition: Unit.h:1744
Unit * GetVictim() const
Definition: Unit.h:715
Unit * GetFirstControlled() const
Definition: Unit.cpp:6414
uint32 HasUnitTypeMask(uint32 mask) const
Definition: Unit.h:736
SpellHistory * GetSpellHistory()
Definition: Unit.h:1457
ObjectGuid GetCharmerGUID() const
Definition: Unit.h:1187
virtual bool HasSpellFocus(Spell const *=nullptr) const
Definition: Unit.h:1449
bool HasAuraTypeWithTriggerSpell(AuraType auratype, uint32 triggerSpell) const
Definition: Unit.cpp:4714
void SetInFront(WorldObject const *target)
Definition: Unit.cpp:12647
ObjectGuid GetBattlePetCompanionGUID() const
Definition: Unit.h:1180
Guardian * GetGuardianPet() const
Definition: Unit.cpp:6046
bool AttackStop()
Definition: Unit.cpp:5781
Map * GetMap() const
Definition: Object.h:624
Unit * GetOwner() const
Definition: Object.cpp:2229
std::string const & GetName() const
Definition: Object.h:555
void SetName(std::string newname)
Definition: Object.h:556
TaggedPosition< Position::XYZ > ActionPosition
Definition: PetPackets.h:192
WorldPacket const * Write() override
Definition: PetPackets.cpp:79
WorldPacket const * Write() override
WorldPacket const * Write() override
void HandlePetCastSpellOpcode(WorldPackets::Spells::PetCastSpell &petCastSpell)
Definition: PetHandler.cpp:668
bool CheckStableMaster(ObjectGuid guid)
Definition: PetHandler.cpp:440
void HandleDismissCritter(WorldPackets::Pet::DismissCritter &dismissCritter)
Definition: PetHandler.cpp:41
void HandlePetStopAttack(WorldPackets::Pet::PetStopAttack &packet)
Definition: PetHandler.cpp:118
Player * GetPlayer() const
void HandlePetAction(WorldPackets::Pet::PetAction &packet)
Definition: PetHandler.cpp:64
void HandlePetRename(WorldPackets::Pet::PetRename &packet)
Definition: PetHandler.cpp:529
void HandleQueryPetName(WorldPackets::Query::QueryPetName &packet)
Definition: PetHandler.cpp:410
void HandleRequestPetInfo(WorldPackets::Pet::RequestPetInfo &requestPetInfo)
Definition: PetHandler.cpp:767
void HandlePetSpellAutocastOpcode(WorldPackets::Pet::PetSpellAutocast &packet)
Definition: PetHandler.cpp:618
void SendPacket(WorldPacket const *packet, bool forced=false)
Send a packet to the client.
uint32 GetAccountId() const
void HandlePetSetAction(WorldPackets::Pet::PetSetAction &packet)
Definition: PetHandler.cpp:463
void SendQueryPetNameResponse(ObjectGuid guid)
Definition: PetHandler.cpp:415
Player * _player
void SendPetNameInvalid(uint32 error, std::string const &name, Optional< DeclinedName > const &declinedName)
Definition: PetHandler.cpp:757
void HandlePetAbandon(WorldPackets::Pet::PetAbandon &packet)
Definition: PetHandler.cpp:608
void HandlePetActionHelper(Unit *pet, ObjectGuid guid1, uint32 spellid, uint16 flag, ObjectGuid guid2, Position const &pos)
Definition: PetHandler.cpp:141
TC_GAME_API bool GetName(uint32 accountId, std::string &name)
time_t GetGameTime()
Definition: GameTime.cpp:44
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 &)
std::string ToString(Type &&val, Params &&... params)
void SaveStayPosition()
Definition: Unit.cpp:12596
void SetIsCommandFollow(bool val)
Definition: Unit.cpp:12586
bool IsCommandAttack()
Definition: Unit.cpp:12581
uint32 GetPetNumber() const
Definition: CharmInfo.h:94
void SetActionBar(uint8 index, uint32 spellOrAction, ActiveStates type)
Definition: CharmInfo.h:112
void SetIsAtStay(bool val)
Definition: Unit.cpp:12617
void SetIsFollowing(bool val)
Definition: Unit.cpp:12627
void SetIsReturning(bool val)
Definition: Unit.cpp:12637
void SetSpellAutocast(SpellInfo const *spellInfo, bool state)
Definition: CharmInfo.cpp:273
void ToggleCreatureAutocast(SpellInfo const *spellInfo, bool apply)
Definition: CharmInfo.cpp:217
void SetCommandState(CommandStates st)
Definition: CharmInfo.h:97
void SetIsCommandAttack(bool val)
Definition: Unit.cpp:12576
std::string Name
Definition: PetDefines.h:140
Optional< DeclinedName > DeclinedNames
Definition: PetPackets.h:157