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