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 // Can't attack if owner is pacified
186 {
187 // pet->SendPetCastFail(spellid, SPELL_FAILED_PACIFIED);
189 return;
190 }
191
192 // only place where pet can be player
193 Unit* TargetUnit = ObjectAccessor::GetUnit(*_player, guid2);
194 if (!TargetUnit)
195 return;
196
197 if (Unit* owner = pet->GetOwner())
198 if (!owner->IsValidAttackTarget(TargetUnit))
199 return;
200
201 // This is true if pet has no target or has target but targets differs.
202 if (pet->GetVictim() != TargetUnit || !pet->GetCharmInfo()->IsCommandAttack())
203 {
204 if (pet->GetVictim())
205 pet->AttackStop();
206
207 if (pet->GetTypeId() != TYPEID_PLAYER && pet->ToCreature()->IsAIEnabled())
208 {
209 charmInfo->SetIsCommandAttack(true);
210 charmInfo->SetIsAtStay(false);
211 charmInfo->SetIsFollowing(false);
212 charmInfo->SetIsCommandFollow(false);
213 charmInfo->SetIsReturning(false);
214
215 CreatureAI* AI = pet->ToCreature()->AI();
216 if (PetAI* petAI = dynamic_cast<PetAI*>(AI))
217 petAI->_AttackStart(TargetUnit); // force target switch
218 else
219 AI->AttackStart(TargetUnit);
220
221 // 10% chance to play special pet attack talk, else growl
222 if (pet->IsPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != TargetUnit && urand(0, 100) < 10)
224 else
225 {
226 // 90% chance for pet and 100% chance for charmed creature
227 pet->SendPetAIReaction(guid1);
228 }
229 }
230 else // charmed player
231 {
232 charmInfo->SetIsCommandAttack(true);
233 charmInfo->SetIsAtStay(false);
234 charmInfo->SetIsFollowing(false);
235 charmInfo->SetIsCommandFollow(false);
236 charmInfo->SetIsReturning(false);
237
238 pet->Attack(TargetUnit, true);
239 pet->SendPetAIReaction(guid1);
240 }
241 }
242 break;
243 }
244 case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet)
245 if (pet->GetCharmerGUID() == GetPlayer()->GetGUID())
247 else if (pet->GetOwnerGUID() == GetPlayer()->GetGUID())
248 {
249 ASSERT(pet->GetTypeId() == TYPEID_UNIT);
250 if (pet->IsPet())
251 {
252 if (((Pet*)pet)->getPetType() == HUNTER_PET)
254 else
256 }
257 else if (pet->HasUnitTypeMask(UNIT_MASK_MINION))
258 {
259 ((Minion*)pet)->UnSummon();
260 }
261 }
262 break;
263 case COMMAND_MOVE_TO:
264 pet->StopMoving();
265 pet->GetMotionMaster()->Clear();
266 pet->GetMotionMaster()->MovePoint(0, pos);
268
269 charmInfo->SetIsCommandAttack(false);
270 charmInfo->SetIsAtStay(true);
271 charmInfo->SetIsFollowing(false);
272 charmInfo->SetIsReturning(false);
273 charmInfo->SaveStayPosition();
274 break;
275 default:
276 TC_LOG_ERROR("entities.pet", "WORLD: unknown PET flag Action {} and spellid {}.", uint32(flag), spellid);
277 }
278 break;
279 case ACT_REACTION: // 0x6
280 switch (spellid)
281 {
282 case REACT_PASSIVE: // passive
283 pet->AttackStop();
284 [[fallthrough]];
285 case REACT_DEFENSIVE: // recovery
286 case REACT_AGGRESSIVE: // activete
287 if (pet->GetTypeId() == TYPEID_UNIT)
288 pet->ToCreature()->SetReactState(ReactStates(spellid));
289 break;
290 }
291 break;
292 case ACT_DISABLED: // 0x81 spell (disabled), ignore
293 case ACT_PASSIVE: // 0x01
294 case ACT_ENABLED: // 0xC1 spell
295 {
296 Unit* unit_target = nullptr;
297
298 if (!guid2.IsEmpty())
299 unit_target = ObjectAccessor::GetUnit(*_player, guid2);
300
301 // do not cast unknown spells
302 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid, pet->GetMap()->GetDifficultyID());
303 if (!spellInfo)
304 {
305 TC_LOG_ERROR("spells.pet", "WORLD: unknown PET spell id {}", spellid);
306 return;
307 }
308
309 for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects())
310 {
311 if (spellEffectInfo.TargetA.GetTarget() == TARGET_UNIT_SRC_AREA_ENEMY || spellEffectInfo.TargetA.GetTarget() == TARGET_UNIT_DEST_AREA_ENEMY || spellEffectInfo.TargetA.GetTarget() == TARGET_DEST_DYNOBJ_ENEMY)
312 return;
313 }
314
315 // do not cast not learned spells
316 if (!pet->HasSpell(spellid) || spellInfo->IsPassive())
317 return;
318
319 // Clear the flags as if owner clicked 'attack'. AI will reset them
320 // after AttackStart, even if spell failed
321 if (pet->GetCharmInfo())
322 {
323 pet->GetCharmInfo()->SetIsAtStay(false);
324 pet->GetCharmInfo()->SetIsCommandAttack(true);
325 pet->GetCharmInfo()->SetIsReturning(false);
326 pet->GetCharmInfo()->SetIsFollowing(false);
327 }
328
329 Spell* spell = new Spell(pet, spellInfo, TRIGGERED_NONE);
330
331 SpellCastResult result = spell->CheckPetCast(unit_target);
332
333 // auto turn to target unless possessed
334 if (result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->isPossessed() && !pet->IsVehicle())
335 {
336 if (unit_target)
337 {
338 if (!pet->HasSpellFocus())
339 pet->SetInFront(unit_target);
340 if (Player* player = unit_target->ToPlayer())
341 pet->SendUpdateToPlayer(player);
342 }
343 else if (Unit* unit_target2 = spell->m_targets.GetUnitTarget())
344 {
345 if (!pet->HasSpellFocus())
346 pet->SetInFront(unit_target2);
347 if (Player* player = unit_target2->ToPlayer())
348 pet->SendUpdateToPlayer(player);
349 }
350
351 if (Unit* powner = pet->GetCharmerOrOwner())
352 if (Player* player = powner->ToPlayer())
353 pet->SendUpdateToPlayer(player);
354
355 result = SPELL_CAST_OK;
356 }
357
358 if (result == SPELL_CAST_OK)
359 {
360 unit_target = spell->m_targets.GetUnitTarget();
361
362 // 10% chance to play special pet attack talk, else growl
363 // actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
364 if (pet->IsPet() && (((Pet*)pet)->getPetType() == SUMMON_PET) && (pet != unit_target) && (urand(0, 100) < 10))
366 else
367 {
368 pet->SendPetAIReaction(guid1);
369 }
370
371 if (unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->isPossessed() && !pet->IsVehicle())
372 {
373 // This is true if pet has no target or has target but targets differs.
374 if (pet->GetVictim() != unit_target)
375 {
376 if (CreatureAI* AI = pet->ToCreature()->AI())
377 {
378 if (PetAI* petAI = dynamic_cast<PetAI*>(AI))
379 petAI->_AttackStart(unit_target); // force victim switch
380 else
381 AI->AttackStart(unit_target);
382 }
383 }
384 }
385
386 spell->prepare(spell->m_targets);
387 }
388 else
389 {
390 if (pet->isPossessed() || pet->IsVehicle())
391 Spell::SendCastResult(GetPlayer(), spellInfo, spell->m_SpellVisual, spell->m_castId, result);
392 else
393 spell->SendPetCastResult(result);
394
395 if (!pet->GetSpellHistory()->HasCooldown(spellid))
396 pet->GetSpellHistory()->ResetCooldown(spellid, true);
397
398 spell->finish(result);
399 delete spell;
400
401 // reset specific flags in case of spell fail. AI will reset other flags
402 if (pet->GetCharmInfo())
403 pet->GetCharmInfo()->SetIsCommandAttack(false);
404 }
405 break;
406 }
407 default:
408 TC_LOG_ERROR("entities.pet", "WORLD: unknown PET flag Action {} and spellid {}.", uint32(flag), spellid);
409 }
410}
411
416
418{
420
421 response.UnitGUID = guid;
422
424 {
425 response.Allow = true;
426 response.Timestamp = *unit->m_unitData->PetNameTimestamp;
427 response.Name = unit->GetName();
428
429 if (Pet* pet = unit->ToPet())
430 {
431 if (DeclinedName const* names = pet->GetDeclinedNames())
432 {
433 response.HasDeclined = true;
434 response.DeclinedNames = *names;
435 }
436 }
437 }
438
439 _player->SendDirectMessage(response.Write());
440}
441
443{
444 // spell case or GM
445 if (guid == GetPlayer()->GetGUID())
446 {
447 if (!GetPlayer()->IsGameMaster() && !GetPlayer()->HasAuraType(SPELL_AURA_OPEN_STABLE))
448 {
449 TC_LOG_DEBUG("entities.player.cheat", "{} attempt open stable in cheating way.", guid.ToString());
450 return false;
451 }
452 }
453 // stable master case
454 else
455 {
456 if (!GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_STABLEMASTER, UNIT_NPC_FLAG_2_NONE))
457 {
458 TC_LOG_DEBUG("entities.player", "Stablemaster {} not found or you can't interact with him.", guid.ToString());
459 return false;
460 }
461 }
462 return true;
463}
464
466{
467 ObjectGuid petguid = packet.PetGUID;
468 Unit* pet = ObjectAccessor::GetUnit(*_player, petguid);
469
470 if (!pet || pet != _player->GetFirstControlled())
471 {
472 TC_LOG_ERROR("entities.pet", "HandlePetSetAction: Unknown {} or owner ({})", petguid.ToString(), _player->GetGUID().ToString());
473 return;
474 }
475
476 CharmInfo* charmInfo = pet->GetCharmInfo();
477 if (!charmInfo)
478 {
479 TC_LOG_ERROR("entities.pet", "WorldSession::HandlePetSetAction: object {} is considered pet-like but doesn't have a charminfo!", pet->GetGUID().ToString());
480 return;
481 }
482
483 std::vector<Unit*> pets;
484 for (Unit* controlled : _player->m_Controlled)
485 if (controlled->GetEntry() == pet->GetEntry() && controlled->IsAlive())
486 pets.push_back(controlled);
487
488 uint32 position = packet.Index;
489 uint32 actionData = packet.Action;
490
491 uint32 spell_id = UNIT_ACTION_BUTTON_ACTION(actionData);
492 uint8 act_state = UNIT_ACTION_BUTTON_TYPE(actionData);
493
494 TC_LOG_DEBUG("entities.pet", "Player {} has changed pet spell action. Position: {}, Spell: {}, State: 0x{:X}",
495 _player->GetName(), position, spell_id, uint32(act_state));
496
497 for (Unit* petControlled : pets)
498 {
499 //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
500 if (!((act_state == ACT_ENABLED || act_state == ACT_DISABLED || act_state == ACT_PASSIVE) && spell_id && !petControlled->HasSpell(spell_id)))
501 {
502 if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id, petControlled->GetMap()->GetDifficultyID()))
503 {
504 //sign for autocast
505 if (act_state == ACT_ENABLED)
506 {
507 if (petControlled->GetTypeId() == TYPEID_UNIT && petControlled->IsPet())
508 ((Pet*)petControlled)->ToggleAutocast(spellInfo, true);
509 else
510 for (Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr)
511 if ((*itr)->GetEntry() == petControlled->GetEntry())
512 (*itr)->GetCharmInfo()->ToggleCreatureAutocast(spellInfo, true);
513 }
514 //sign for no/turn off autocast
515 else if (act_state == ACT_DISABLED)
516 {
517 if (petControlled->GetTypeId() == TYPEID_UNIT && petControlled->IsPet())
518 ((Pet*)petControlled)->ToggleAutocast(spellInfo, false);
519 else
520 for (Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr)
521 if ((*itr)->GetEntry() == petControlled->GetEntry())
522 (*itr)->GetCharmInfo()->ToggleCreatureAutocast(spellInfo, false);
523 }
524 }
525
526 charmInfo->SetActionBar(position, spell_id, ActiveStates(act_state));
527 }
528 }
529}
530
532{
533 ObjectGuid petguid = packet.RenameData.PetGUID;
534
535 std::string name = packet.RenameData.NewName;
536 Optional<DeclinedName> const& declinedname = packet.RenameData.DeclinedNames;
537
538 PetStable* petStable = _player->GetPetStable();
539 Pet* pet = ObjectAccessor::GetPet(*_player, petguid);
540 // check it!
541 if (!pet || !pet->IsPet() || ((Pet*)pet)->getPetType() != HUNTER_PET ||
543 pet->GetOwnerGUID() != _player->GetGUID() || !pet->GetCharmInfo() ||
544 !petStable || !petStable->GetCurrentPet() || petStable->GetCurrentPet()->PetNumber != pet->GetCharmInfo()->GetPetNumber())
545 return;
546
548 if (res != PET_NAME_SUCCESS)
549 {
550 SendPetNameInvalid(res, name, {});
551 return;
552 }
553
554 if (sObjectMgr->IsReservedName(name))
555 {
557 return;
558 }
559
560 pet->SetName(name);
561
563
565
566 petStable->GetCurrentPet()->Name = name;
567 petStable->GetCurrentPet()->WasRenamed = true;
568
569 if (declinedname)
570 {
571 std::wstring wname;
572 if (!Utf8toWStr(name, wname))
573 return;
574
575 if (!ObjectMgr::CheckDeclinedNames(wname, *declinedname))
576 {
578 return;
579 }
580 }
581
582 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
583 if (declinedname)
584 {
586 stmt->setUInt32(0, pet->GetCharmInfo()->GetPetNumber());
587 trans->Append(stmt);
588
589 stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_PET_DECLINEDNAME);
590 stmt->setUInt32(0, pet->GetCharmInfo()->GetPetNumber());
591 stmt->setUInt64(1, _player->GetGUID().GetCounter());
592
593 for (uint8 i = 0; i < 5; i++)
594 stmt->setString(i + 2, declinedname->name[i]);
595
596 trans->Append(stmt);
597 }
598
600 stmt->setString(0, name);
601 stmt->setUInt64(1, _player->GetGUID().GetCounter());
602 stmt->setUInt32(2, pet->GetCharmInfo()->GetPetNumber());
603 trans->Append(stmt);
604
605 CharacterDatabase.CommitTransaction(trans);
606
608}
609
611{
612 // pet/charmed
614 if (pet && pet->ToPet() && pet->ToPet()->getPetType() == HUNTER_PET)
615 {
617 }
618}
619
621{
622 if (Pet* pet = _player->GetPet())
623 {
624 if (pet->IsHunterPet() && pet->m_unitData->PetNumber == petAbandonByNumber.PetNumber)
626 }
627 else
628 {
629 _player->DeletePetFromDB(petAbandonByNumber.PetNumber);
630 }
631}
632
634{
636 if (!pet)
637 {
638 TC_LOG_ERROR("entities.pet", "WorldSession::HandlePetSpellAutocastOpcode: Pet {} not found.", packet.PetGUID.ToString());
639 return;
640 }
641
642 if (pet != _player->GetGuardianPet() && pet != _player->GetCharmed())
643 {
644 TC_LOG_ERROR("entities.pet", "WorldSession::HandlePetSpellAutocastOpcode: {} isn't pet of player {} ({}).",
645 packet.PetGUID.ToString(), GetPlayer()->GetName(), GetPlayer()->GetGUID().ToString());
646 return;
647 }
648
649 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(packet.SpellID, pet->GetMap()->GetDifficultyID());
650 if (!spellInfo)
651 {
652 TC_LOG_ERROR("spells.pet", "WorldSession::HandlePetSpellAutocastOpcode: Unknown spell id {} used by {}.", packet.SpellID, packet.PetGUID.ToString());
653 return;
654 }
655
656 std::vector<Unit*> pets;
657 for (Unit* controlled : _player->m_Controlled)
658 if (controlled->GetEntry() == pet->GetEntry() && controlled->IsAlive())
659 pets.push_back(controlled);
660
661 for (Unit* petControlled : pets)
662 {
663 // do not add not learned spells/ passive spells
664 if (!petControlled->HasSpell(packet.SpellID) || !spellInfo->IsAutocastable())
665 return;
666
667 CharmInfo* charmInfo = petControlled->GetCharmInfo();
668 if (!charmInfo)
669 {
670 TC_LOG_ERROR("entities.pet", "WorldSession::HandlePetSpellAutocastOpcode: object {} is considered pet-like but doesn't have a charminfo!", petControlled->GetGUID().ToString());
671 return;
672 }
673
674 if (petControlled->IsPet())
675 petControlled->ToPet()->ToggleAutocast(spellInfo, packet.AutocastEnabled);
676 else
677 charmInfo->ToggleCreatureAutocast(spellInfo, packet.AutocastEnabled);
678
679 charmInfo->SetSpellAutocast(spellInfo, packet.AutocastEnabled);
680 }
681}
682
684{
685 Unit* caster = ObjectAccessor::GetUnit(*_player, petCastSpell.PetGUID);
686 if (!caster)
687 {
688 TC_LOG_ERROR("entities.pet", "WorldSession::HandlePetCastSpellOpcode: Caster {} not found.", petCastSpell.PetGUID.ToString());
689 return;
690 }
691
692 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(petCastSpell.Cast.SpellID, caster->GetMap()->GetDifficultyID());
693 if (!spellInfo)
694 {
695 TC_LOG_ERROR("spells.pet", "WorldSession::HandlePetCastSpellOpcode: unknown spell id {} tried to cast by {}",
696 petCastSpell.Cast.SpellID, petCastSpell.PetGUID.ToString());
697 return;
698 }
699
700 // This opcode is also sent from charmed and possessed units (players and creatures)
701 if (caster != _player->GetGuardianPet() && caster != _player->GetCharmed())
702 {
703 TC_LOG_ERROR("spells.pet", "WorldSession::HandlePetCastSpellOpcode: {} isn't pet of player {} ({}).", petCastSpell.PetGUID.ToString(), GetPlayer()->GetName(), GetPlayer()->GetGUID().ToString());
704 return;
705 }
706
707 SpellCastTargets targets(caster, petCastSpell.Cast);
708
709 TriggerCastFlags triggerCastFlags = TRIGGERED_NONE;
710
711 if (spellInfo->IsPassive())
712 return;
713
714 // cast only learned spells
715 if (!caster->HasSpell(spellInfo->Id))
716 {
717 bool allow = false;
718
719 // allow casting of spells triggered by clientside periodic trigger auras
721 {
722 allow = true;
723 triggerCastFlags = TRIGGERED_FULL_MASK;
724 }
725
726 if (!allow)
727 return;
728 }
729
730 Spell* spell = new Spell(caster, spellInfo, triggerCastFlags);
731 spell->m_fromClient = true;
732 std::ranges::copy(petCastSpell.Cast.Misc, std::ranges::begin(spell->m_misc.Raw.Data));
733 spell->m_targets = targets;
734
735 SpellCastResult result = spell->CheckPetCast(nullptr);
736
737 if (result == SPELL_CAST_OK)
738 {
739 if (Creature* creature = caster->ToCreature())
740 {
741 if (Pet* pet = creature->ToPet())
742 {
743 // 10% chance to play special pet attack talk, else growl
744 // actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
745 if (pet->getPetType() == SUMMON_PET && (urand(0, 100) < 10))
746 pet->SendPetTalk(PET_TALK_SPECIAL_SPELL);
747 else
748 pet->SendPetAIReaction(petCastSpell.PetGUID);
749 }
750 }
751
753 spellPrepare.ClientCastID = petCastSpell.Cast.CastID;
754 spellPrepare.ServerCastID = spell->m_castId;
755 SendPacket(spellPrepare.Write());
756
757 spell->prepare(targets);
758 }
759 else
760 {
761 spell->SendPetCastResult(result);
762
763 if (!caster->GetSpellHistory()->HasCooldown(spellInfo))
764 caster->GetSpellHistory()->ResetCooldown(spellInfo->Id, true);
765
766 spell->finish(result);
767 delete spell;
768 }
769}
770
771void WorldSession::SendPetNameInvalid(uint32 error, const std::string& name, Optional<DeclinedName> const& declinedName)
772{
774 petNameInvalid.Result = error;
775 petNameInvalid.RenameData.NewName = name;
776 petNameInvalid.RenameData.DeclinedNames = declinedName;
777
778 SendPacket(petNameInvalid.Write());
779}
780
782{
783 // Handle the packet CMSG_REQUEST_PET_INFO - sent when player does ingame /reload command
784
785 // Packet sent when player has a pet
786 if (_player->GetPet())
788 else if (Unit* charm = _player->GetCharmed())
789 {
790 // Packet sent when player has a possessed unit
791 if (charm->HasUnitState(UNIT_STATE_POSSESSED))
793 // Packet sent when player controlling a vehicle
794 else if (charm->HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED) && charm->HasUnitFlag(UNIT_FLAG_POSSESSED))
796 // Packet sent when player has a charmed unit
797 else
799 }
800}
@ 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:80
@ GROUP_UPDATE_FLAG_PET_NAME
Definition Group.h:153
#define TC_LOG_DEBUG(filterType__, message__,...)
Definition Log.h:181
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
@ MOTION_PRIORITY_NORMAL
@ TYPEID_UNIT
Definition ObjectGuid.h:43
@ TYPEID_PLAYER
Definition ObjectGuid.h:44
#define sObjectMgr
Definition ObjectMgr.h:1885
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition Optional.h:25
#define PET_FOLLOW_DIST
Definition PetDefines.h:98
@ SUMMON_PET
Definition PetDefines.h:31
@ HUNTER_PET
Definition PetDefines.h:32
@ PET_TALK_SPECIAL_SPELL
Definition PetDefines.h:94
@ PET_TALK_ATTACK
Definition PetDefines.h:95
@ 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
@ 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_MOD_PACIFY
@ 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:357
@ CURRENT_CHANNELED_SPELL
Definition Unit.h:599
@ CURRENT_GENERIC_SPELL
Definition Unit.h:598
@ UNIT_STATE_POSSESSED
Definition Unit.h:277
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:174
CreatureAI * AI() const
Definition Creature.h:228
Difficulty GetDifficultyID() const
Definition Map.h:360
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:1835
PetType getPetType() const
Definition Pet.h:51
ObjectGuid GetSummonedBattlePetGUID() const
Definition Player.h:2995
void SendDirectMessage(WorldPacket const *data) const
Definition Player.cpp:6283
void CharmSpellInitialize()
Definition Player.cpp:22583
void DeletePetFromDB(uint32 petNumber)
Definition Player.cpp:22179
PetStable * GetPetStable()
Definition Player.h:1355
void PetSpellInitialize()
Definition Player.cpp:22471
Pet * GetPet() const
Definition Player.cpp:22060
void StopCastingCharm()
Definition Player.cpp:22269
void RemovePet(Pet *pet, PetSaveMode mode, bool returnreagent=false)
Definition Player.cpp:22084
void SetBattlePetData(BattlePets::BattlePet const *pet=nullptr)
Definition Player.cpp:22251
void PossessSpellInitialize()
Definition Player.cpp:22510
void VehicleSpellInitialize()
Definition Player.cpp:22536
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:587
Definition Spell.h:277
bool m_fromClient
Definition Spell.h:606
SpellCastTargets m_targets
Definition Spell.h:651
union Spell::@321 m_misc
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:4699
SpellCastResult prepare(SpellCastTargets const &targets, AuraEffect const *triggeredByAura=nullptr)
Definition Spell.cpp:3419
uint32 Data[3]
Definition Spell.h:646
SpellCastResult CheckPetCast(Unit *target)
Definition Spell.cpp:6976
void SendPetCastResult(SpellCastResult result, int32 *param1=nullptr, int32 *param2=nullptr) const
Definition Spell.cpp:4682
void finish(SpellCastResult result=SPELL_CAST_OK)
Definition Spell.cpp:4342
struct Spell::@321::@324 Raw
SpellCastVisual m_SpellVisual
Definition Spell.h:650
ObjectGuid m_castId
Definition Spell.h:604
virtual void UnSummon(uint32 msTime=0)
Definition Unit.h:635
Unit * GetCharmed() const
Definition Unit.h:1212
bool IsVehicle() const
Definition Unit.h:754
bool HasPetFlag(UnitPetFlag flags) const
Definition Unit.h:891
Pet * ToPet()
Definition Unit.h:1822
ObjectGuid GetOwnerGUID() const override
Definition Unit.h:1191
MotionMaster * GetMotionMaster()
Definition Unit.h:1723
bool IsPet() const
Definition Unit.h:751
void SetPetNameTimestamp(uint32 timestamp)
Definition Unit.h:1246
bool IsAlive() const
Definition Unit.h:1185
void StopMoving()
Definition Unit.cpp:10680
TempSummon * ToTempSummon()
Definition Unit.h:1828
CharmInfo * GetCharmInfo()
Definition Unit.h:1242
ControlList m_Controlled
Definition Unit.h:1232
virtual bool HasSpell(uint32) const
Definition Unit.h:1084
Unit * GetCharmerOrOwner() const
Definition Unit.h:1221
bool IsAIEnabled() const
Definition Unit.h:666
bool Attack(Unit *victim, bool meleeAttack)
Definition Unit.cpp:5853
bool isPossessed() const
Definition Unit.h:1237
bool IsSummon() const
Definition Unit.h:749
void SendPetTalk(uint32 pettalk)
Definition Unit.cpp:10644
bool HasAuraType(AuraType auraType) const
Definition Unit.cpp:4814
void RemovePetFlag(UnitPetFlag flags)
Definition Unit.h:893
ObjectGuid GetCritterGUID() const
Definition Unit.h:1199
bool IsMounted() const
Definition Unit.h:912
void SendPetAIReaction(ObjectGuid guid)
Definition Unit.cpp:10656
virtual float GetFollowAngle() const
Definition Unit.h:1816
Unit * GetVictim() const
Definition Unit.h:726
Unit * GetFirstControlled() const
Definition Unit.cpp:6599
uint32 HasUnitTypeMask(uint32 mask) const
Definition Unit.h:747
SpellHistory * GetSpellHistory()
Definition Unit.h:1498
ObjectGuid GetCharmerGUID() const
Definition Unit.h:1208
virtual bool HasSpellFocus(Spell const *=nullptr) const
Definition Unit.h:1490
bool HasAuraTypeWithTriggerSpell(AuraType auratype, uint32 triggerSpell) const
Definition Unit.cpp:4854
void SetInFront(WorldObject const *target)
Definition Unit.cpp:13283
ObjectGuid GetBattlePetCompanionGUID() const
Definition Unit.h:1201
Guardian * GetGuardianPet() const
Definition Unit.cpp:6231
bool AttackStop()
Definition Unit.cpp:5965
void InterruptSpell(CurrentSpellTypes spellType, bool withDelayed=true, bool withInstant=true)
Definition Unit.cpp:3159
Spell * GetCurrentSpell(CurrentSpellTypes spellType) const
Definition Unit.h:1466
Map * GetMap() const
Definition Object.h:411
Unit * GetOwner() const
Definition Object.cpp:1598
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 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:13232
void SetIsCommandFollow(bool val)
Definition Unit.cpp:13222
bool IsCommandAttack()
Definition Unit.cpp:13217
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:13253
void SetIsFollowing(bool val)
Definition Unit.cpp:13263
void SetIsReturning(bool val)
Definition Unit.cpp:13273
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:13212
std::string Name
Definition PetDefines.h:151
Optional< DeclinedName > DeclinedNames
Definition PetPackets.h:167