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 "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->InterruptSpell(CURRENT_GENERIC_SPELL, false, false);
171 if (Spell const* channeledSpell = pet->GetCurrentSpell(CURRENT_CHANNELED_SPELL); channeledSpell && !channeledSpell->GetSpellInfo()->HasAttribute(SPELL_ATTR9_CHANNEL_PERSISTS_ON_PET_FOLLOW))
172 pet->InterruptSpell(CURRENT_CHANNELED_SPELL, true, true);
174
176 charmInfo->SetIsCommandAttack(false);
177 charmInfo->SetIsAtStay(false);
178 charmInfo->SetIsReturning(true);
179 charmInfo->SetIsCommandFollow(true);
180 charmInfo->SetIsFollowing(false);
181 break;
182 case COMMAND_ATTACK: // spellid = 1792 - ATTACK
183 {
184 // only place where pet can be player
185 Unit* TargetUnit = ObjectAccessor::GetUnit(*_player, guid2);
186 if (!TargetUnit)
187 return;
188
189 if (Unit* owner = pet->GetOwner())
190 if (!owner->IsValidAttackTarget(TargetUnit))
191 return;
192
193 // This is true if pet has no target or has target but targets differs.
194 if (pet->GetVictim() != TargetUnit || !pet->GetCharmInfo()->IsCommandAttack())
195 {
196 if (pet->GetVictim())
197 pet->AttackStop();
198
199 if (pet->GetTypeId() != TYPEID_PLAYER && pet->ToCreature()->IsAIEnabled())
200 {
201 charmInfo->SetIsCommandAttack(true);
202 charmInfo->SetIsAtStay(false);
203 charmInfo->SetIsFollowing(false);
204 charmInfo->SetIsCommandFollow(false);
205 charmInfo->SetIsReturning(false);
206
207 CreatureAI* AI = pet->ToCreature()->AI();
208 if (PetAI* petAI = dynamic_cast<PetAI*>(AI))
209 petAI->_AttackStart(TargetUnit); // force target switch
210 else
211 AI->AttackStart(TargetUnit);
212
213 // 10% chance to play special pet attack talk, else growl
214 if (pet->IsPet() && pet->ToPet()->getPetType() == SUMMON_PET && pet != TargetUnit && roll_chance(10))
216 else
217 {
218 // 90% chance for pet and 100% chance for charmed creature
219 pet->SendPetAIReaction(guid1);
220 }
221 }
222 else // charmed player
223 {
224 charmInfo->SetIsCommandAttack(true);
225 charmInfo->SetIsAtStay(false);
226 charmInfo->SetIsFollowing(false);
227 charmInfo->SetIsCommandFollow(false);
228 charmInfo->SetIsReturning(false);
229
230 pet->Attack(TargetUnit, true);
231 pet->SendPetAIReaction(guid1);
232 }
233 }
234 break;
235 }
236 case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet)
237 if (pet->GetCharmerGUID() == GetPlayer()->GetGUID())
239 else if (pet->GetOwnerGUID() == GetPlayer()->GetGUID())
240 {
241 ASSERT(pet->GetTypeId() == TYPEID_UNIT);
242 if (pet->IsPet())
243 {
244 if (pet->ToPet()->getPetType() == HUNTER_PET)
246 else
247 {
248 pet->SendPetDismissSound();
250 }
251 }
252 else if (pet->HasUnitTypeMask(UNIT_MASK_MINION))
253 {
254 ((Minion*)pet)->UnSummon();
255 }
256 }
257 break;
258 case COMMAND_MOVE_TO:
259 pet->StopMoving();
260 pet->GetMotionMaster()->Clear();
261 pet->GetMotionMaster()->MovePoint(0, pos);
263
264 charmInfo->SetIsCommandAttack(false);
265 charmInfo->SetIsAtStay(true);
266 charmInfo->SetIsFollowing(false);
267 charmInfo->SetIsReturning(false);
268 charmInfo->SaveStayPosition();
269 break;
270 default:
271 TC_LOG_ERROR("entities.pet", "WORLD: unknown PET flag Action {} and spellid {}.", uint32(flag), spellid);
272 }
273 break;
274 case ACT_REACTION: // 0x6
275 switch (spellid)
276 {
277 case REACT_PASSIVE: // passive
278 pet->AttackStop();
279 [[fallthrough]];
280 case REACT_DEFENSIVE: // recovery
281 case REACT_AGGRESSIVE: // activete
282 if (pet->GetTypeId() == TYPEID_UNIT)
283 pet->ToCreature()->SetReactState(ReactStates(spellid));
284 break;
285 }
286 break;
287 case ACT_DISABLED: // 0x81 spell (disabled), ignore
288 case ACT_PASSIVE: // 0x01
289 case ACT_ENABLED: // 0xC1 spell
290 {
291 Unit* unit_target = nullptr;
292
293 if (!guid2.IsEmpty())
294 unit_target = ObjectAccessor::GetUnit(*_player, guid2);
295
296 // do not cast unknown spells
297 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid, pet->GetMap()->GetDifficultyID());
298 if (!spellInfo)
299 {
300 TC_LOG_ERROR("spells.pet", "WORLD: unknown PET spell id {}", spellid);
301 return;
302 }
303
304 for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects())
305 {
306 if (spellEffectInfo.TargetA.GetTarget() == TARGET_UNIT_SRC_AREA_ENEMY || spellEffectInfo.TargetA.GetTarget() == TARGET_UNIT_DEST_AREA_ENEMY || spellEffectInfo.TargetA.GetTarget() == TARGET_DEST_DYNOBJ_ENEMY)
307 return;
308 }
309
310 // do not cast not learned spells
311 if (!pet->HasSpell(spellid) || spellInfo->IsPassive())
312 return;
313
314 // Clear the flags as if owner clicked 'attack'. AI will reset them
315 // after AttackStart, even if spell failed
316 if (pet->GetCharmInfo())
317 {
318 pet->GetCharmInfo()->SetIsAtStay(false);
319 pet->GetCharmInfo()->SetIsCommandAttack(true);
320 pet->GetCharmInfo()->SetIsReturning(false);
321 pet->GetCharmInfo()->SetIsFollowing(false);
322 }
323
324 Spell* spell = new Spell(pet, spellInfo, TRIGGERED_NONE);
325
326 SpellCastResult result = spell->CheckPetCast(unit_target);
327
328 // auto turn to target unless possessed
329 if (result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->isPossessed() && !pet->IsVehicle())
330 {
331 if (unit_target)
332 {
333 if (!pet->HasSpellFocus())
334 pet->SetInFront(unit_target);
335 if (Player* player = unit_target->ToPlayer())
336 pet->SendUpdateToPlayer(player);
337 }
338 else if (Unit* unit_target2 = spell->m_targets.GetUnitTarget())
339 {
340 if (!pet->HasSpellFocus())
341 pet->SetInFront(unit_target2);
342 if (Player* player = unit_target2->ToPlayer())
343 pet->SendUpdateToPlayer(player);
344 }
345
346 if (Unit* powner = pet->GetCharmerOrOwner())
347 if (Player* player = powner->ToPlayer())
348 pet->SendUpdateToPlayer(player);
349
350 result = SPELL_CAST_OK;
351 }
352
353 if (result == SPELL_CAST_OK)
354 {
355 unit_target = spell->m_targets.GetUnitTarget();
356
357 // 10% chance to play special pet attack talk, else growl
358 // actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
359 if (pet->IsPet() && pet->ToPet()->getPetType() == SUMMON_PET && pet != unit_target && roll_chance(10))
361 else
362 {
363 pet->SendPetAIReaction(guid1);
364 }
365
366 if (unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->isPossessed() && !pet->IsVehicle())
367 {
368 // This is true if pet has no target or has target but targets differs.
369 if (pet->GetVictim() != unit_target)
370 {
371 if (CreatureAI* AI = pet->ToCreature()->AI())
372 {
373 if (PetAI* petAI = dynamic_cast<PetAI*>(AI))
374 petAI->_AttackStart(unit_target); // force victim switch
375 else
376 AI->AttackStart(unit_target);
377 }
378 }
379 }
380
381 spell->prepare(spell->m_targets);
382 }
383 else
384 {
385 if (pet->isPossessed() || pet->IsVehicle())
386 Spell::SendCastResult(GetPlayer(), spellInfo, spell->m_SpellVisual, spell->m_castId, result);
387 else
388 spell->SendPetCastResult(result);
389
390 if (!pet->GetSpellHistory()->HasCooldown(spellid))
391 pet->GetSpellHistory()->ResetCooldown(spellid, true);
392
393 spell->finish(result);
394 delete spell;
395
396 // reset specific flags in case of spell fail. AI will reset other flags
397 if (pet->GetCharmInfo())
398 pet->GetCharmInfo()->SetIsCommandAttack(false);
399 }
400 break;
401 }
402 default:
403 TC_LOG_ERROR("entities.pet", "WORLD: unknown PET flag Action {} and spellid {}.", uint32(flag), spellid);
404 }
405}
406
411
413{
415
416 response.UnitGUID = guid;
417
419 {
420 response.Allow = true;
421 response.Timestamp = *unit->m_unitData->PetNameTimestamp;
422 response.Name = unit->GetName();
423
424 if (Pet* pet = unit->ToPet())
425 {
426 if (DeclinedName const* names = pet->GetDeclinedNames())
427 {
428 response.HasDeclined = true;
429 response.DeclinedNames = *names;
430 }
431 }
432 }
433
434 _player->SendDirectMessage(response.Write());
435}
436
438{
439 // spell case or GM
440 if (guid == GetPlayer()->GetGUID())
441 {
442 if (!GetPlayer()->IsGameMaster() && !GetPlayer()->HasAuraType(SPELL_AURA_OPEN_STABLE))
443 {
444 TC_LOG_DEBUG("entities.player.cheat", "{} attempt open stable in cheating way.", guid.ToString());
445 return false;
446 }
447 }
448 // stable master case
449 else
450 {
451 if (!GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_STABLEMASTER, UNIT_NPC_FLAG_2_NONE))
452 {
453 TC_LOG_DEBUG("entities.player", "Stablemaster {} not found or you can't interact with him.", guid.ToString());
454 return false;
455 }
456 }
457 return true;
458}
459
461{
462 ObjectGuid petguid = packet.PetGUID;
463 Unit* pet = ObjectAccessor::GetUnit(*_player, petguid);
464
465 if (!pet || pet != _player->GetFirstControlled())
466 {
467 TC_LOG_ERROR("entities.pet", "HandlePetSetAction: Unknown {} or owner ({})", petguid.ToString(), _player->GetGUID().ToString());
468 return;
469 }
470
471 CharmInfo* charmInfo = pet->GetCharmInfo();
472 if (!charmInfo)
473 {
474 TC_LOG_ERROR("entities.pet", "WorldSession::HandlePetSetAction: object {} is considered pet-like but doesn't have a charminfo!", pet->GetGUID().ToString());
475 return;
476 }
477
478 std::vector<Unit*> pets;
479 for (Unit* controlled : _player->m_Controlled)
480 if (controlled->GetEntry() == pet->GetEntry() && controlled->IsAlive())
481 pets.push_back(controlled);
482
483 uint32 position = packet.Index;
484 uint32 actionData = packet.Action;
485
486 uint32 spell_id = UNIT_ACTION_BUTTON_ACTION(actionData);
487 uint8 act_state = UNIT_ACTION_BUTTON_TYPE(actionData);
488
489 TC_LOG_DEBUG("entities.pet", "Player {} has changed pet spell action. Position: {}, Spell: {}, State: 0x{:X}",
490 _player->GetName(), position, spell_id, uint32(act_state));
491
492 for (Unit* petControlled : pets)
493 {
494 //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
495 if (!((act_state == ACT_ENABLED || act_state == ACT_DISABLED || act_state == ACT_PASSIVE) && spell_id && !petControlled->HasSpell(spell_id)))
496 {
497 if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id, petControlled->GetMap()->GetDifficultyID()))
498 {
499 //sign for autocast
500 if (act_state == ACT_ENABLED)
501 {
502 if (petControlled->GetTypeId() == TYPEID_UNIT && petControlled->IsPet())
503 ((Pet*)petControlled)->ToggleAutocast(spellInfo, true);
504 else
505 for (Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr)
506 if ((*itr)->GetEntry() == petControlled->GetEntry())
507 (*itr)->GetCharmInfo()->ToggleCreatureAutocast(spellInfo, true);
508 }
509 //sign for no/turn off autocast
510 else if (act_state == ACT_DISABLED)
511 {
512 if (petControlled->GetTypeId() == TYPEID_UNIT && petControlled->IsPet())
513 ((Pet*)petControlled)->ToggleAutocast(spellInfo, false);
514 else
515 for (Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr)
516 if ((*itr)->GetEntry() == petControlled->GetEntry())
517 (*itr)->GetCharmInfo()->ToggleCreatureAutocast(spellInfo, false);
518 }
519 }
520
521 charmInfo->SetActionBar(position, spell_id, ActiveStates(act_state));
522 }
523 }
524}
525
527{
528 ObjectGuid petguid = packet.RenameData.PetGUID;
529
530 std::string name = packet.RenameData.NewName;
531 Optional<DeclinedName> const& declinedname = packet.RenameData.DeclinedNames;
532
533 PetStable* petStable = _player->GetPetStable();
534 Pet* pet = ObjectAccessor::GetPet(*_player, petguid);
535 // check it!
536 if (!pet || !pet->IsPet() || ((Pet*)pet)->getPetType() != HUNTER_PET ||
538 pet->GetOwnerGUID() != _player->GetGUID() || !pet->GetCharmInfo() ||
539 !petStable || !petStable->GetCurrentPet() || petStable->GetCurrentPet()->PetNumber != pet->GetCharmInfo()->GetPetNumber())
540 return;
541
543 if (res != PET_NAME_SUCCESS)
544 {
545 SendPetNameInvalid(res, name, {});
546 return;
547 }
548
549 if (sObjectMgr->IsReservedName(name))
550 {
552 return;
553 }
554
555 pet->SetName(name);
556
558
560
561 petStable->GetCurrentPet()->Name = name;
562 petStable->GetCurrentPet()->WasRenamed = true;
563
564 if (declinedname)
565 {
566 std::wstring wname;
567 if (!Utf8toWStr(name, wname))
568 return;
569
570 if (!ObjectMgr::CheckDeclinedNames(wname, *declinedname))
571 {
573 return;
574 }
575 }
576
577 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
578 if (declinedname)
579 {
581 stmt->setUInt32(0, pet->GetCharmInfo()->GetPetNumber());
582 trans->Append(stmt);
583
584 stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_PET_DECLINEDNAME);
585 stmt->setUInt32(0, pet->GetCharmInfo()->GetPetNumber());
586 stmt->setUInt64(1, _player->GetGUID().GetCounter());
587
588 for (uint8 i = 0; i < 5; i++)
589 stmt->setString(i + 2, declinedname->name[i]);
590
591 trans->Append(stmt);
592 }
593
595 stmt->setString(0, name);
596 stmt->setUInt64(1, _player->GetGUID().GetCounter());
597 stmt->setUInt32(2, pet->GetCharmInfo()->GetPetNumber());
598 trans->Append(stmt);
599
600 CharacterDatabase.CommitTransaction(trans);
601
603}
604
606{
607 // pet/charmed
609 if (pet && pet->ToPet() && pet->ToPet()->getPetType() == HUNTER_PET)
610 {
612 }
613}
614
616{
617 if (Pet* pet = _player->GetPet())
618 {
619 if (pet->IsHunterPet() && pet->m_unitData->PetNumber == petAbandonByNumber.PetNumber)
621 }
622 else
623 {
624 _player->DeletePetFromDB(petAbandonByNumber.PetNumber);
625 }
626}
627
629{
631 if (!pet)
632 {
633 TC_LOG_ERROR("entities.pet", "WorldSession::HandlePetSpellAutocastOpcode: Pet {} not found.", packet.PetGUID.ToString());
634 return;
635 }
636
637 if (pet != _player->GetGuardianPet() && pet != _player->GetCharmed())
638 {
639 TC_LOG_ERROR("entities.pet", "WorldSession::HandlePetSpellAutocastOpcode: {} isn't pet of player {} ({}).",
640 packet.PetGUID.ToString(), GetPlayer()->GetName(), GetPlayer()->GetGUID().ToString());
641 return;
642 }
643
644 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(packet.SpellID, pet->GetMap()->GetDifficultyID());
645 if (!spellInfo)
646 {
647 TC_LOG_ERROR("spells.pet", "WorldSession::HandlePetSpellAutocastOpcode: Unknown spell id {} used by {}.", packet.SpellID, packet.PetGUID.ToString());
648 return;
649 }
650
651 std::vector<Unit*> pets;
652 for (Unit* controlled : _player->m_Controlled)
653 if (controlled->GetEntry() == pet->GetEntry() && controlled->IsAlive())
654 pets.push_back(controlled);
655
656 for (Unit* petControlled : pets)
657 {
658 // do not add not learned spells/ passive spells
659 if (!petControlled->HasSpell(packet.SpellID) || !spellInfo->IsAutocastable())
660 return;
661
662 CharmInfo* charmInfo = petControlled->GetCharmInfo();
663 if (!charmInfo)
664 {
665 TC_LOG_ERROR("entities.pet", "WorldSession::HandlePetSpellAutocastOpcode: object {} is considered pet-like but doesn't have a charminfo!", petControlled->GetGUID().ToString());
666 return;
667 }
668
669 if (petControlled->IsPet())
670 petControlled->ToPet()->ToggleAutocast(spellInfo, packet.AutocastEnabled);
671 else
672 charmInfo->ToggleCreatureAutocast(spellInfo, packet.AutocastEnabled);
673
674 charmInfo->SetSpellAutocast(spellInfo, packet.AutocastEnabled);
675 }
676}
677
679{
680 Unit* caster = ObjectAccessor::GetUnit(*_player, petCastSpell.PetGUID);
681 if (!caster)
682 {
683 TC_LOG_ERROR("entities.pet", "WorldSession::HandlePetCastSpellOpcode: Caster {} not found.", petCastSpell.PetGUID.ToString());
684 return;
685 }
686
687 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(petCastSpell.Cast.SpellID, caster->GetMap()->GetDifficultyID());
688 if (!spellInfo)
689 {
690 TC_LOG_ERROR("spells.pet", "WorldSession::HandlePetCastSpellOpcode: unknown spell id {} tried to cast by {}",
691 petCastSpell.Cast.SpellID, petCastSpell.PetGUID.ToString());
692 return;
693 }
694
695 // This opcode is also sent from charmed and possessed units (players and creatures)
696 if (caster != _player->GetGuardianPet() && caster != _player->GetCharmed())
697 {
698 TC_LOG_ERROR("spells.pet", "WorldSession::HandlePetCastSpellOpcode: {} isn't pet of player {} ({}).", petCastSpell.PetGUID.ToString(), GetPlayer()->GetName(), GetPlayer()->GetGUID().ToString());
699 return;
700 }
701
702 SpellCastTargets targets(caster, petCastSpell.Cast);
703
704 TriggerCastFlags triggerCastFlags = TRIGGERED_NONE;
705
706 if (spellInfo->IsPassive())
707 return;
708
709 // cast only learned spells
710 if (!caster->HasSpell(spellInfo->Id))
711 {
712 bool allow = false;
713
714 // allow casting of spells triggered by clientside periodic trigger auras
716 {
717 allow = true;
718 triggerCastFlags = TRIGGERED_FULL_MASK;
719 }
720
721 if (!allow)
722 return;
723 }
724
725 if (petCastSpell.Cast.MoveUpdate)
727
728 Spell* spell = new Spell(caster, spellInfo, triggerCastFlags);
729 spell->m_fromClient = true;
730 std::ranges::copy(petCastSpell.Cast.Misc, std::ranges::begin(spell->m_misc.Raw.Data));
731 spell->InitExplicitTargets(targets);
732
733 SpellCastResult result = spell->CheckPetCast(nullptr);
734
735 if (result == SPELL_CAST_OK)
736 {
737 if (Creature* creature = caster->ToCreature())
738 {
739 if (Pet* pet = creature->ToPet())
740 {
741 // 10% chance to play special pet attack sound, else growl
742 // actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
743 if (pet->getPetType() == SUMMON_PET && roll_chance(10))
744 pet->SendPetActionSound(PET_ACTION_SPECIAL_SPELL);
745 else
746 pet->SendPetAIReaction(petCastSpell.PetGUID);
747 }
748 }
749
751 spellPrepare.ClientCastID = petCastSpell.Cast.CastID;
752 spellPrepare.ServerCastID = spell->m_castId;
753 SendPacket(spellPrepare.Write());
754
755 spell->prepare(targets);
756 }
757 else
758 {
759 spell->SendPetCastResult(result);
760
761 if (!caster->GetSpellHistory()->HasCooldown(spellInfo))
762 caster->GetSpellHistory()->ResetCooldown(spellInfo->Id, true);
763
764 spell->finish(result);
765 delete spell;
766 }
767}
768
769void WorldSession::SendPetNameInvalid(uint32 error, const std::string& name, Optional<DeclinedName> const& declinedName)
770{
772 petNameInvalid.Result = error;
773 petNameInvalid.RenameData.NewName = name;
774 petNameInvalid.RenameData.DeclinedNames = declinedName;
775
776 SendPacket(petNameInvalid.Write());
777}
778
780{
781 // Handle the packet CMSG_REQUEST_PET_INFO - sent when player does ingame /reload command
782
783 // Packet sent when player has a pet
784 if (_player->GetPet())
786 else if (Unit* charm = _player->GetCharmed())
787 {
788 // Packet sent when player has a possessed unit
789 if (charm->HasUnitState(UNIT_STATE_POSSESSED))
791 // Packet sent when player controlling a vehicle
792 else if (charm->HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED) && charm->HasUnitFlag(UNIT_FLAG_POSSESSED))
794 // Packet sent when player has a charmed unit
795 else
797 }
798}
@ CHAR_UPD_CHAR_PET_NAME
@ CHAR_INS_CHAR_PET_DECLINEDNAME
@ CHAR_DEL_CHAR_PET_DECLINEDNAME
#define UNIT_ACTION_BUTTON_ACTION(X)
Definition CharmInfo.h:32
#define UNIT_ACTION_BUTTON_TYPE(X)
Definition CharmInfo.h:33
SQLTransaction< CharacterDatabaseConnection > CharacterDatabaseTransaction
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
uint8_t uint8
Definition Define.h:156
uint16_t uint16
Definition Define.h:155
uint32_t uint32
Definition Define.h:154
#define ASSERT
Definition Errors.h:72
@ GROUP_UPDATE_FLAG_PET_NAME
Definition Group.h:153
#define TC_LOG_DEBUG(filterType__, message__,...)
Definition Log.h:173
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:182
@ MOTION_PRIORITY_NORMAL
@ TYPEID_UNIT
Definition ObjectGuid.h:43
@ TYPEID_PLAYER
Definition ObjectGuid.h:44
#define sObjectMgr
Definition ObjectMgr.h:1873
@ CMSG_MOVE_STOP
Definition Opcodes.h:691
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition Optional.h:25
#define PET_FOLLOW_DIST
Definition PetDefines.h:98
@ PET_ACTION_ATTACK
Definition PetDefines.h:95
@ PET_ACTION_SPECIAL_SPELL
Definition PetDefines.h:94
@ SUMMON_PET
Definition PetDefines.h:31
@ HUNTER_PET
Definition PetDefines.h:32
@ PET_SAVE_AS_DELETED
Definition PetDefines.h:42
@ PET_SAVE_NOT_IN_SLOT
Definition PetDefines.h:48
bool roll_chance(float chance, PseudoRandomDistributionState &state)
Definition Random.cpp:131
@ SPELL_ATTR9_CHANNEL_PERSISTS_ON_PET_FOLLOW
@ 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_OPEN_STABLE
@ SPELL_AURA_PERIODIC_TRIGGER_SPELL_FROM_CLIENT
TriggerCastFlags
@ TRIGGERED_FULL_MASK
Used when doing CastSpell with triggered == true.
@ TRIGGERED_NONE
Not triggered.
#define sSpellMgr
Definition SpellMgr.h:812
@ UNIT_PET_FLAG_CAN_BE_RENAMED
ReactStates
@ REACT_DEFENSIVE
@ REACT_PASSIVE
@ REACT_AGGRESSIVE
ActiveStates
@ ACT_REACTION
@ ACT_COMMAND
@ ACT_ENABLED
@ ACT_PASSIVE
@ ACT_DISABLED
@ UNIT_NPC_FLAG_STABLEMASTER
@ UNIT_NPC_FLAG_2_NONE
@ COMMAND_ATTACK
@ COMMAND_MOVE_TO
@ COMMAND_ABANDON
@ COMMAND_STAY
@ COMMAND_FOLLOW
@ UNIT_FLAG_POSSESSED
@ UNIT_FLAG_PLAYER_CONTROLLED
@ UNIT_MASK_MINION
Definition Unit.h:371
@ CURRENT_CHANNELED_SPELL
Definition Unit.h:611
@ CURRENT_GENERIC_SPELL
Definition Unit.h:610
@ UNIT_STATE_POSSESSED
Definition Unit.h:291
bool Utf8toWStr(char const *utf8str, size_t csize, wchar_t *wstr, size_t &wsize)
Definition Util.cpp:336
ObjectGuid const & GetGUID() const
Definition BaseEntity.h:163
TypeID GetTypeId() const
Definition BaseEntity.h:166
void SendUpdateToPlayer(Player *player) const
void AttackStart(Unit *victim) override
== Triggered Actions Requested ==================
void SetReactState(ReactStates st)
Definition Creature.h:180
CreatureAI * AI() const
Definition Creature.h:235
Difficulty GetDifficultyID() const
Definition Map.h:363
void MoveFollow(Unit *target, float dist, Optional< ChaseAngle > angle={}, Optional< Milliseconds > duration={}, bool ignoreTargetWalk=false, MovementSlot slot=MOTION_SLOT_ACTIVE, Scripting::v2::ActionResultSetter< MovementStopReason > &&scriptResult={})
void MovePoint(uint32 id, Position const &pos, bool generatePath=true, Optional< float > finalOrient={}, Optional< float > speed={}, MovementWalkRunSpeedSelectionMode speedSelectionMode=MovementWalkRunSpeedSelectionMode::Default, Optional< float > closeEnoughDistance={}, Optional< MovementFadeObject > fadeObject={}, Scripting::v2::ActionResultSetter< MovementStopReason > &&scriptResult={})
LowType GetCounter() const
Definition ObjectGuid.h:336
bool IsEmpty() const
Definition ObjectGuid.h:362
std::string ToString() const
static bool CheckDeclinedNames(const std::wstring &w_ownname, DeclinedName const &names)
static PetNameInvalidReason CheckPetName(std::string_view name)
Player * ToPlayer()
Definition Object.h:126
uint32 GetEntry() const
Definition Object.h:89
Creature * ToCreature()
Definition Object.h:121
Definition PetAI.h:30
PetInfo * GetCurrentPet()
Definition PetDefines.h:173
Definition Pet.h:40
void SetGroupUpdateFlag(uint32 flag)
Definition Pet.cpp:1843
PetType getPetType() const
Definition Pet.h:51
ObjectGuid GetSummonedBattlePetGUID() const
Definition Player.h:2979
void SendDirectMessage(WorldPacket const *data) const
Definition Player.cpp:6193
void CharmSpellInitialize()
Definition Player.cpp:22530
void DeletePetFromDB(uint32 petNumber)
Definition Player.cpp:22126
PetStable * GetPetStable()
Definition Player.h:1339
void PetSpellInitialize()
Definition Player.cpp:22418
Pet * GetPet() const
Definition Player.cpp:22007
void StopCastingCharm()
Definition Player.cpp:22216
void RemovePet(Pet *pet, PetSaveMode mode, bool returnreagent=false)
Definition Player.cpp:22031
void SetBattlePetData(BattlePets::BattlePet const *pet=nullptr)
Definition Player.cpp:22198
void PossessSpellInitialize()
Definition Player.cpp:22457
void VehicleSpellInitialize()
Definition Player.cpp:22483
void setString(uint8 index, std::string &&value)
void setUInt32(uint8 index, uint32 value)
void setUInt64(uint8 index, uint64 value)
Unit * GetUnitTarget() const
Definition Spell.cpp:186
void ResetCooldown(uint32 spellId, bool update=false)
bool HasCooldown(SpellInfo const *spellInfo, uint32 itemId=0) const
bool IsAutocastable() const
uint32 const Id
Definition SpellInfo.h:328
bool IsPassive() const
bool HasAttribute(SpellAttr0 attribute) const
Definition SpellInfo.h:456
std::vector< SpellEffectInfo > const & GetEffects() const
Definition SpellInfo.h:589
Definition Spell.h:277
bool m_fromClient
Definition Spell.h:606
SpellCastTargets m_targets
Definition Spell.h:651
union Spell::@323 m_misc
SpellCastResult prepare(SpellCastTargets const &targets, AuraEffect const *triggeredByAura=nullptr)
Definition Spell.cpp:3432
uint32 Data[3]
Definition Spell.h:646
SpellCastResult CheckPetCast(Unit *target)
Definition Spell.cpp:6917
struct Spell::@323::@326 Raw
void InitExplicitTargets(SpellCastTargets const &targets)
Definition Spell.cpp:621
void SendPetCastResult(SpellCastResult result, int32 *param1=nullptr, int32 *param2=nullptr) const
Definition Spell.cpp:4624
static void SendCastResult(Player const *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:4641
void finish(SpellCastResult result=SPELL_CAST_OK)
Definition Spell.cpp:4359
SpellCastVisual m_SpellVisual
Definition Spell.h:650
ObjectGuid m_castId
Definition Spell.h:604
virtual void UnSummon(uint32 msTime=0)
Definition Unit.h:647
Unit * GetCharmed() const
Definition Unit.h:1232
bool IsVehicle() const
Definition Unit.h:766
bool HasPetFlag(UnitPetFlag flags) const
Definition Unit.h:906
Pet * ToPet()
Definition Unit.h:1838
void SendPetAIReaction(ObjectGuid guid) const
Definition Unit.cpp:10729
ObjectGuid GetOwnerGUID() const override
Definition Unit.h:1211
void StopMoving(bool force=false)
Definition Unit.cpp:10753
MotionMaster * GetMotionMaster()
Definition Unit.h:1750
bool IsPet() const
Definition Unit.h:763
void InterruptSpell(CurrentSpellTypes spellType, bool withDelayed=true, bool withInstant=true, SpellCastResult result=SPELL_FAILED_INTERRUPTED, Optional< SpellCastResult > resultOther={}, ObjectGuid const &interrupter=ObjectGuid::Empty)
Definition Unit.cpp:3158
void SetPetNameTimestamp(uint32 timestamp)
Definition Unit.h:1266
bool IsAlive() const
Definition Unit.h:1205
TempSummon * ToTempSummon()
Definition Unit.h:1844
CharmInfo * GetCharmInfo()
Definition Unit.h:1262
ControlList m_Controlled
Definition Unit.h:1252
virtual bool HasSpell(uint32) const
Definition Unit.h:1099
Unit * GetCharmerOrOwner() const
Definition Unit.h:1241
bool IsAIEnabled() const
Definition Unit.h:678
bool Attack(Unit *victim, bool meleeAttack)
Definition Unit.cpp:5872
bool isPossessed() const
Definition Unit.h:1257
bool IsSummon() const
Definition Unit.h:761
void SendPetActionSound(PetAction action) const
Definition Unit.cpp:10712
void RemovePetFlag(UnitPetFlag flags)
Definition Unit.h:908
ObjectGuid GetCritterGUID() const
Definition Unit.h:1219
bool IsMounted() const
Definition Unit.h:927
virtual float GetFollowAngle() const
Definition Unit.h:1832
Unit * GetVictim() const
Definition Unit.h:738
Unit * GetFirstControlled() const
Definition Unit.cpp:6618
uint32 HasUnitTypeMask(uint32 mask) const
Definition Unit.h:759
SpellHistory * GetSpellHistory()
Definition Unit.h:1521
ObjectGuid GetCharmerGUID() const
Definition Unit.h:1228
virtual bool HasSpellFocus(Spell const *=nullptr) const
Definition Unit.h:1513
bool HasAuraTypeWithTriggerSpell(AuraType auratype, uint32 triggerSpell) const
Definition Unit.cpp:4872
void SetInFront(WorldObject const *target)
Definition Unit.cpp:13379
ObjectGuid GetBattlePetCompanionGUID() const
Definition Unit.h:1221
Guardian * GetGuardianPet() const
Definition Unit.cpp:6250
bool AttackStop()
Definition Unit.cpp:5984
void SendPetDismissSound() const
Definition Unit.cpp:10720
Spell * GetCurrentSpell(CurrentSpellTypes spellType) const
Definition Unit.h:1490
Map * GetMap() const
Definition Object.h:411
Unit * GetOwner() const
Definition Object.cpp:1603
std::string const & GetName() const
Definition Object.h:342
void SetName(std::string newname)
Definition Object.h:343
TaggedPosition< Position::XYZ > ActionPosition
Definition PetPackets.h:202
WorldPacket const * Write() override
WorldPacket const * Write() override
WorldPacket const * Write() override
void HandlePetCastSpellOpcode(WorldPackets::Spells::PetCastSpell &petCastSpell)
void HandleMovementOpcode(OpcodeClient opcode, MovementInfo &movementInfo)
void HandlePetAbandonByNumber(WorldPackets::Pet::PetAbandonByNumber const &petAbandonByNumber)
bool CheckStableMaster(ObjectGuid guid)
void HandleDismissCritter(WorldPackets::Pet::DismissCritter &dismissCritter)
void HandlePetStopAttack(WorldPackets::Pet::PetStopAttack &packet)
Player * GetPlayer() const
void HandlePetAction(WorldPackets::Pet::PetAction &packet)
void HandlePetRename(WorldPackets::Pet::PetRename &packet)
void HandleQueryPetName(WorldPackets::Query::QueryPetName &packet)
void HandleRequestPetInfo(WorldPackets::Pet::RequestPetInfo &requestPetInfo)
void HandlePetSpellAutocastOpcode(WorldPackets::Pet::PetSpellAutocast &packet)
void SendPacket(WorldPacket const *packet, bool forced=false)
Send a packet to the client.
uint32 GetAccountId() const
void HandlePetSetAction(WorldPackets::Pet::PetSetAction &packet)
void SendQueryPetNameResponse(ObjectGuid guid)
Player * _player
void SendPetNameInvalid(uint32 error, std::string const &name, Optional< DeclinedName > const &declinedName)
void HandlePetAbandon(WorldPackets::Pet::PetAbandon &packet)
void HandlePetActionHelper(Unit *pet, ObjectGuid guid1, uint32 spellid, uint16 flag, ObjectGuid guid2, Position const &pos)
time_t GetGameTime()
Definition GameTime.cpp:52
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:13328
void SetIsCommandFollow(bool val)
Definition Unit.cpp:13318
bool IsCommandAttack()
Definition Unit.cpp:13313
uint32 GetPetNumber() const
Definition CharmInfo.h:93
void SetActionBar(uint8 index, uint32 spellOrAction, ActiveStates type)
Definition CharmInfo.h:110
void SetIsAtStay(bool val)
Definition Unit.cpp:13349
void SetIsFollowing(bool val)
Definition Unit.cpp:13359
void SetIsReturning(bool val)
Definition Unit.cpp:13369
void SetSpellAutocast(SpellInfo const *spellInfo, bool state)
void ToggleCreatureAutocast(SpellInfo const *spellInfo, bool apply)
void SetCommandState(CommandStates st)
Definition CharmInfo.h:96
void SetIsCommandAttack(bool val)
Definition Unit.cpp:13308
std::string Name
Definition PetDefines.h:151
Optional< DeclinedName > DeclinedNames
Definition PetPackets.h:167
Optional< MovementInfo > MoveUpdate