aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Handlers/PetHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/game/Handlers/PetHandler.cpp')
-rwxr-xr-xsrc/server/game/Handlers/PetHandler.cpp879
1 files changed, 879 insertions, 0 deletions
diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp
new file mode 100755
index 00000000000..68ce3153450
--- /dev/null
+++ b/src/server/game/Handlers/PetHandler.cpp
@@ -0,0 +1,879 @@
+/*
+ * Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "Common.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "Log.h"
+#include "Opcodes.h"
+#include "Spell.h"
+#include "ObjectAccessor.h"
+#include "CreatureAI.h"
+#include "Util.h"
+#include "Pet.h"
+#include "World.h"
+#include "Group.h"
+#include "SpellInfo.h"
+
+void WorldSession::HandleDismissCritter(WorldPacket &recv_data)
+{
+ uint64 guid;
+ recv_data >> guid;
+
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_DISMISS_CRITTER for GUID " UI64FMTD, guid);
+
+ Unit* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
+
+ if (!pet)
+ {
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "Vanitypet (guid: %u) does not exist - player '%s' (guid: %u / account: %u) attempted to dismiss it (possibly lagged out)",
+ uint32(GUID_LOPART(guid)), GetPlayer()->GetName(), GetPlayer()->GetGUIDLow(), GetAccountId());
+ return;
+ }
+
+ if (_player->GetCritterGUID() == pet->GetGUID())
+ {
+ if (pet->GetTypeId() == TYPEID_UNIT && pet->ToCreature()->isSummon())
+ pet->ToTempSummon()->UnSummon();
+ }
+}
+
+void WorldSession::HandlePetAction(WorldPacket & recv_data)
+{
+ uint64 guid1;
+ uint32 data;
+ uint64 guid2;
+ recv_data >> guid1; //pet guid
+ recv_data >> data;
+ recv_data >> guid2; //tag guid
+
+ uint32 spellid = UNIT_ACTION_BUTTON_ACTION(data);
+ uint8 flag = UNIT_ACTION_BUTTON_TYPE(data); //delete = 0x07 CastSpell = C1
+
+ // used also for charmed creature
+ Unit* pet= ObjectAccessor::GetUnit(*_player, guid1);
+ sLog->outDetail("HandlePetAction: Pet %u - flag: %u, spellid: %u, target: %u.", uint32(GUID_LOPART(guid1)), uint32(flag), spellid, uint32(GUID_LOPART(guid2)));
+
+ if (!pet)
+ {
+ sLog->outError("HandlePetAction: Pet (GUID: %u) doesn't exist for player '%s'", uint32(GUID_LOPART(guid1)), GetPlayer()->GetName());
+ return;
+ }
+
+ if (pet != GetPlayer()->GetFirstControlled())
+ {
+ sLog->outError("HandlePetAction: Pet (GUID: %u) does not belong to player '%s'", uint32(GUID_LOPART(guid1)), GetPlayer()->GetName());
+ return;
+ }
+
+ if (!pet->isAlive())
+ {
+ SpellInfo const* spell = (flag == ACT_ENABLED || flag == ACT_PASSIVE) ? sSpellMgr->GetSpellInfo(spellid) : NULL;
+ if (!spell)
+ return;
+ if (!(spell->Attributes & SPELL_ATTR0_CASTABLE_WHILE_DEAD))
+ return;
+ }
+
+ //TODO: allow control charmed player?
+ if (pet->GetTypeId() == TYPEID_PLAYER && !(flag == ACT_COMMAND && spellid == COMMAND_ATTACK))
+ return;
+
+ if (GetPlayer()->m_Controlled.size() == 1)
+ HandlePetActionHelper(pet, guid1, spellid, flag, guid2);
+ else
+ {
+ //If a pet is dismissed, m_Controlled will change
+ std::vector<Unit*> controlled;
+ for (Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr)
+ if ((*itr)->GetEntry() == pet->GetEntry() && (*itr)->isAlive())
+ controlled.push_back(*itr);
+ for (std::vector<Unit*>::iterator itr = controlled.begin(); itr != controlled.end(); ++itr)
+ HandlePetActionHelper(*itr, guid1, spellid, flag, guid2);
+ }
+}
+
+void WorldSession::HandlePetStopAttack(WorldPacket &recv_data)
+{
+ uint64 guid;
+ recv_data >> guid;
+
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_PET_STOP_ATTACK for GUID " UI64FMTD "", guid);
+
+ Unit* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
+
+ if (!pet)
+ {
+ sLog->outError("HandlePetStopAttack: Pet %u does not exist", uint32(GUID_LOPART(guid)));
+ return;
+ }
+
+ if (pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharm())
+ {
+ sLog->outError("HandlePetStopAttack: Pet GUID %u isn't a pet or charmed creature of player %s", uint32(GUID_LOPART(guid)), GetPlayer()->GetName());
+ return;
+ }
+
+ if (!pet->isAlive())
+ return;
+
+ pet->AttackStop();
+}
+
+void WorldSession::HandlePetActionHelper(Unit* pet, uint64 guid1, uint16 spellid, uint16 flag, uint64 guid2)
+{
+ CharmInfo* charmInfo = pet->GetCharmInfo();
+ if (!charmInfo)
+ {
+ sLog->outError("WorldSession::HandlePetAction(petGuid: " UI64FMTD ", tagGuid: " UI64FMTD ", spellId: %u, flag: %u): object (entry: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!",
+ guid1, guid2, spellid, flag, pet->GetGUIDLow(), pet->GetTypeId());
+ return;
+ }
+
+ switch (flag)
+ {
+ case ACT_COMMAND: //0x07
+ switch (spellid)
+ {
+ case COMMAND_STAY: //flat=1792 //STAY
+ pet->AttackStop();
+ pet->InterruptNonMeleeSpells(false);
+ pet->GetMotionMaster()->Clear(false);
+ pet->GetMotionMaster()->MoveIdle();
+ charmInfo->SetCommandState(COMMAND_STAY);
+
+ charmInfo->SetIsCommandAttack(false);
+ charmInfo->SetIsAtStay(true);
+ charmInfo->SetIsFollowing(false);
+ charmInfo->SetIsReturning(false);
+ charmInfo->SaveStayPosition();
+ break;
+ case COMMAND_FOLLOW: //spellid=1792 //FOLLOW
+ pet->AttackStop();
+ pet->InterruptNonMeleeSpells(false);
+ pet->GetMotionMaster()->MoveFollow(_player, PET_FOLLOW_DIST, pet->GetFollowAngle());
+ charmInfo->SetCommandState(COMMAND_FOLLOW);
+
+ charmInfo->SetIsCommandAttack(false);
+ charmInfo->SetIsAtStay(false);
+ charmInfo->SetIsReturning(true);
+ charmInfo->SetIsFollowing(false);
+ break;
+ case COMMAND_ATTACK: //spellid=1792 //ATTACK
+ {
+ // Can't attack if owner is pacified
+ if (_player->HasAuraType(SPELL_AURA_MOD_PACIFY))
+ {
+ //pet->SendPetCastFail(spellid, SPELL_FAILED_PACIFIED);
+ //TODO: Send proper error message to client
+ return;
+ }
+
+ // only place where pet can be player
+ Unit* TargetUnit = ObjectAccessor::GetUnit(*_player, guid2);
+ if (!TargetUnit)
+ return;
+
+ if (Unit* owner = pet->GetOwner())
+ if (!owner->IsValidAttackTarget(TargetUnit))
+ return;
+
+ // Not let attack through obstructions
+ if (sWorld->getBoolConfig(CONFIG_PET_LOS))
+ {
+ if (!pet->IsWithinLOSInMap(TargetUnit))
+ return;
+ }
+
+ pet->ClearUnitState(UNIT_STAT_FOLLOW);
+ // This is true if pet has no target or has target but targets differs.
+ if (pet->getVictim() != TargetUnit || (pet->getVictim() == TargetUnit && !pet->GetCharmInfo()->IsCommandAttack()))
+ {
+ if (pet->getVictim())
+ pet->AttackStop();
+
+ if (pet->GetTypeId() != TYPEID_PLAYER && pet->ToCreature()->IsAIEnabled)
+ {
+ charmInfo->SetIsCommandAttack(true);
+ charmInfo->SetIsAtStay(false);
+ charmInfo->SetIsFollowing(false);
+ charmInfo->SetIsReturning(false);
+
+ pet->ToCreature()->AI()->AttackStart(TargetUnit);
+
+ //10% chance to play special pet attack talk, else growl
+ if (pet->ToCreature()->isPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != TargetUnit && urand(0, 100) < 10)
+ pet->SendPetTalk((uint32)PET_TALK_ATTACK);
+ else
+ {
+ // 90% chance for pet and 100% chance for charmed creature
+ pet->SendPetAIReaction(guid1);
+ }
+ }
+ else // charmed player
+ {
+ if (pet->getVictim() && pet->getVictim() != TargetUnit)
+ pet->AttackStop();
+
+ charmInfo->SetIsCommandAttack(true);
+ charmInfo->SetIsAtStay(false);
+ charmInfo->SetIsFollowing(false);
+ charmInfo->SetIsReturning(false);
+
+ pet->Attack(TargetUnit, true);
+ pet->SendPetAIReaction(guid1);
+ }
+ }
+ break;
+ }
+ case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet)
+ if (pet->GetCharmerGUID() == GetPlayer()->GetGUID())
+ _player->StopCastingCharm();
+ else if (pet->GetOwnerGUID() == GetPlayer()->GetGUID())
+ {
+ ASSERT(pet->GetTypeId() == TYPEID_UNIT);
+ if (pet->isPet())
+ {
+ if (((Pet*)pet)->getPetType() == HUNTER_PET)
+ GetPlayer()->RemovePet((Pet*)pet, PET_SAVE_AS_DELETED);
+ else
+ //dismissing a summoned pet is like killing them (this prevents returning a soulshard...)
+ pet->setDeathState(CORPSE);
+ }
+ else if (pet->HasUnitTypeMask(UNIT_MASK_MINION))
+ {
+ ((Minion*)pet)->UnSummon();
+ }
+ }
+ break;
+ default:
+ sLog->outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid);
+ }
+ break;
+ case ACT_REACTION: // 0x6
+ switch (spellid)
+ {
+ case REACT_PASSIVE: //passive
+ pet->AttackStop();
+
+ case REACT_DEFENSIVE: //recovery
+ case REACT_AGGRESSIVE: //activete
+ if (pet->GetTypeId() == TYPEID_UNIT)
+ pet->ToCreature()->SetReactState(ReactStates(spellid));
+ break;
+ }
+ break;
+ case ACT_DISABLED: // 0x81 spell (disabled), ignore
+ case ACT_PASSIVE: // 0x01
+ case ACT_ENABLED: // 0xC1 spell
+ {
+ Unit* unit_target = NULL;
+
+ if (guid2)
+ unit_target = ObjectAccessor::GetUnit(*_player, guid2);
+
+ // do not cast unknown spells
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid);
+ if (!spellInfo)
+ {
+ sLog->outError("WORLD: unknown PET spell id %i", spellid);
+ return;
+ }
+
+ if (spellInfo->StartRecoveryCategory > 0)
+ if (pet->GetCharmInfo() && pet->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo))
+ return;
+
+ for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
+ {
+ if (spellInfo->Effects[i].TargetA.GetTarget() == TARGET_UNIT_SRC_AREA_ENEMY || spellInfo->Effects[i].TargetA.GetTarget() == TARGET_UNIT_DEST_AREA_ENEMY || spellInfo->Effects[i].TargetA.GetTarget() == TARGET_DEST_DYNOBJ_ENEMY)
+ return;
+ }
+
+ // do not cast not learned spells
+ if (!pet->HasSpell(spellid) || spellInfo->IsPassive())
+ return;
+
+ // Clear the flags as if owner clicked 'attack'. AI will reset them
+ // after AttackStart, even if spell failed
+ if (pet->GetCharmInfo())
+ {
+ pet->GetCharmInfo()->SetIsAtStay(false);
+ pet->GetCharmInfo()->SetIsCommandAttack(true);
+ pet->GetCharmInfo()->SetIsReturning(false);
+ pet->GetCharmInfo()->SetIsFollowing(false);
+ }
+
+ Spell* spell = new Spell(pet, spellInfo, TRIGGERED_NONE);
+
+ SpellCastResult result = spell->CheckPetCast(unit_target);
+
+ //auto turn to target unless possessed
+ if (result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->isPossessed() && !pet->IsVehicle())
+ {
+ if (unit_target)
+ {
+ pet->SetInFront(unit_target);
+ if (unit_target->GetTypeId() == TYPEID_PLAYER)
+ pet->SendUpdateToPlayer((Player*)unit_target);
+ }
+ else if (Unit* unit_target2 = spell->m_targets.GetUnitTarget())
+ {
+ pet->SetInFront(unit_target2);
+ if (unit_target2->GetTypeId() == TYPEID_PLAYER)
+ pet->SendUpdateToPlayer((Player*)unit_target2);
+ }
+ if (Unit* powner = pet->GetCharmerOrOwner())
+ if (powner->GetTypeId() == TYPEID_PLAYER)
+ pet->SendUpdateToPlayer(powner->ToPlayer());
+ result = SPELL_CAST_OK;
+ }
+
+ if (result == SPELL_CAST_OK)
+ {
+ pet->ToCreature()->AddCreatureSpellCooldown(spellid);
+
+ unit_target = spell->m_targets.GetUnitTarget();
+
+ //10% chance to play special pet attack talk, else growl
+ //actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
+ if (pet->ToCreature()->isPet() && (((Pet*)pet)->getPetType() == SUMMON_PET) && (pet != unit_target) && (urand(0, 100) < 10))
+ pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL);
+ else
+ {
+ pet->SendPetAIReaction(guid1);
+ }
+
+ if (unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->isPossessed() && !pet->IsVehicle())
+ {
+ // This is true if pet has no target or has target but targets differs.
+ if (pet->getVictim() != unit_target)
+ {
+ if (pet->getVictim())
+ pet->AttackStop();
+ pet->GetMotionMaster()->Clear();
+ if (pet->ToCreature()->IsAIEnabled)
+ pet->ToCreature()->AI()->AttackStart(unit_target);
+ }
+ }
+
+ spell->prepare(&(spell->m_targets));
+ }
+ else
+ {
+ if (pet->isPossessed() || pet->IsVehicle())
+ Spell::SendCastResult(GetPlayer(), spellInfo, 0, result);
+ else
+ pet->SendPetCastFail(spellid, result);
+
+ if (!pet->ToCreature()->HasSpellCooldown(spellid))
+ GetPlayer()->SendClearCooldown(spellid, pet);
+
+ spell->finish(false);
+ delete spell;
+
+ // reset specific flags in case of spell fail. AI will reset other flags
+ if (pet->GetCharmInfo())
+ pet->GetCharmInfo()->SetIsCommandAttack(false);
+ }
+ break;
+ }
+ default:
+ sLog->outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid);
+ }
+}
+
+void WorldSession::HandlePetNameQuery(WorldPacket & recv_data)
+{
+ sLog->outDetail("HandlePetNameQuery. CMSG_PET_NAME_QUERY");
+
+ uint32 petnumber;
+ uint64 petguid;
+
+ recv_data >> petnumber;
+ recv_data >> petguid;
+
+ SendPetNameQuery(petguid, petnumber);
+}
+
+void WorldSession::SendPetNameQuery(uint64 petguid, uint32 petnumber)
+{
+ Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, petguid);
+ if (!pet)
+ {
+ WorldPacket data(SMSG_PET_NAME_QUERY_RESPONSE, (4+1+4+1));
+ data << uint32(petnumber);
+ data << uint8(0);
+ data << uint32(0);
+ data << uint8(0);
+ _player->GetSession()->SendPacket(&data);
+ return;
+ }
+
+ std::string name = pet->GetName();
+
+ WorldPacket data(SMSG_PET_NAME_QUERY_RESPONSE, (4+4+name.size()+1));
+ data << uint32(petnumber);
+ data << name.c_str();
+ data << uint32(pet->GetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP));
+
+ if (pet->isPet() && ((Pet*)pet)->GetDeclinedNames())
+ {
+ data << uint8(1);
+ for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
+ data << ((Pet*)pet)->GetDeclinedNames()->name[i];
+ }
+ else
+ data << uint8(0);
+
+ _player->GetSession()->SendPacket(&data);
+}
+
+bool WorldSession::CheckStableMaster(uint64 guid)
+{
+ // spell case or GM
+ if (guid == GetPlayer()->GetGUID())
+ {
+ if (!GetPlayer()->isGameMaster() && !GetPlayer()->HasAuraType(SPELL_AURA_OPEN_STABLE))
+ {
+ sLog->outStaticDebug("Player (GUID:%u) attempt open stable in cheating way.", GUID_LOPART(guid));
+ return false;
+ }
+ }
+ // stable master case
+ else
+ {
+ if (!GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_STABLEMASTER))
+ {
+ sLog->outStaticDebug("Stablemaster (GUID:%u) not found or you can't interact with him.", GUID_LOPART(guid));
+ return false;
+ }
+ }
+ return true;
+}
+
+void WorldSession::HandlePetSetAction(WorldPacket & recv_data)
+{
+ sLog->outDetail("HandlePetSetAction. CMSG_PET_SET_ACTION");
+
+ uint64 petguid;
+ uint8 count;
+
+ recv_data >> petguid;
+
+ Unit* pet = ObjectAccessor::GetUnit(*_player, petguid);
+
+ if (!pet || pet != _player->GetFirstControlled())
+ {
+ sLog->outError("HandlePetSetAction: Unknown pet (GUID: %u) or pet owner (GUID: %u)", GUID_LOPART(petguid), _player->GetGUIDLow());
+ return;
+ }
+
+ CharmInfo* charmInfo = pet->GetCharmInfo();
+ if (!charmInfo)
+ {
+ sLog->outError("WorldSession::HandlePetSetAction: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId());
+ return;
+ }
+
+ count = (recv_data.size() == 24) ? 2 : 1;
+
+ uint32 position[2];
+ uint32 data[2];
+ bool move_command = false;
+
+ for (uint8 i = 0; i < count; ++i)
+ {
+ recv_data >> position[i];
+ recv_data >> data[i];
+
+ uint8 act_state = UNIT_ACTION_BUTTON_TYPE(data[i]);
+
+ //ignore invalid position
+ if (position[i] >= MAX_UNIT_ACTION_BAR_INDEX)
+ return;
+
+ // in the normal case, command and reaction buttons can only be moved, not removed
+ // at moving count == 2, at removing count == 1
+ // ignore attempt to remove command|reaction buttons (not possible at normal case)
+ if (act_state == ACT_COMMAND || act_state == ACT_REACTION)
+ {
+ if (count == 1)
+ return;
+
+ move_command = true;
+ }
+ }
+
+ // check swap (at command->spell swap client remove spell first in another packet, so check only command move correctness)
+ if (move_command)
+ {
+ uint8 act_state_0 = UNIT_ACTION_BUTTON_TYPE(data[0]);
+ if (act_state_0 == ACT_COMMAND || act_state_0 == ACT_REACTION)
+ {
+ uint32 spell_id_0 = UNIT_ACTION_BUTTON_ACTION(data[0]);
+ UnitActionBarEntry const* actionEntry_1 = charmInfo->GetActionBarEntry(position[1]);
+ if (!actionEntry_1 || spell_id_0 != actionEntry_1->GetAction() ||
+ act_state_0 != actionEntry_1->GetType())
+ return;
+ }
+
+ uint8 act_state_1 = UNIT_ACTION_BUTTON_TYPE(data[1]);
+ if (act_state_1 == ACT_COMMAND || act_state_1 == ACT_REACTION)
+ {
+ uint32 spell_id_1 = UNIT_ACTION_BUTTON_ACTION(data[1]);
+ UnitActionBarEntry const* actionEntry_0 = charmInfo->GetActionBarEntry(position[0]);
+ if (!actionEntry_0 || spell_id_1 != actionEntry_0->GetAction() ||
+ act_state_1 != actionEntry_0->GetType())
+ return;
+ }
+ }
+
+ for (uint8 i = 0; i < count; ++i)
+ {
+ uint32 spell_id = UNIT_ACTION_BUTTON_ACTION(data[i]);
+ uint8 act_state = UNIT_ACTION_BUTTON_TYPE(data[i]);
+
+ sLog->outDetail("Player %s has changed pet spell action. Position: %u, Spell: %u, State: 0x%X", _player->GetName(), position[i], spell_id, uint32(act_state));
+
+ //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
+ if (!((act_state == ACT_ENABLED || act_state == ACT_DISABLED || act_state == ACT_PASSIVE) && spell_id && !pet->HasSpell(spell_id)))
+ {
+ if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id))
+ {
+ //sign for autocast
+ if (act_state == ACT_ENABLED)
+ {
+ if (pet->GetTypeId() == TYPEID_UNIT && pet->ToCreature()->isPet())
+ ((Pet*)pet)->ToggleAutocast(spellInfo, true);
+ else
+ for (Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr)
+ if ((*itr)->GetEntry() == pet->GetEntry())
+ (*itr)->GetCharmInfo()->ToggleCreatureAutocast(spellInfo, true);
+ }
+ //sign for no/turn off autocast
+ else if (act_state == ACT_DISABLED)
+ {
+ if (pet->GetTypeId() == TYPEID_UNIT && pet->ToCreature()->isPet())
+ ((Pet*)pet)->ToggleAutocast(spellInfo, false);
+ else
+ for (Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr)
+ if ((*itr)->GetEntry() == pet->GetEntry())
+ (*itr)->GetCharmInfo()->ToggleCreatureAutocast(spellInfo, false);
+ }
+ }
+
+ charmInfo->SetActionBar(position[i], spell_id, ActiveStates(act_state));
+ }
+ }
+}
+
+void WorldSession::HandlePetRename(WorldPacket & recv_data)
+{
+ sLog->outDetail("HandlePetRename. CMSG_PET_RENAME");
+
+ uint64 petguid;
+ uint8 isdeclined;
+
+ std::string name;
+ DeclinedName declinedname;
+
+ recv_data >> petguid;
+ recv_data >> name;
+ recv_data >> isdeclined;
+
+ Pet* pet = ObjectAccessor::FindPet(petguid);
+ // check it!
+ if (!pet || !pet->isPet() || ((Pet*)pet)->getPetType()!= HUNTER_PET ||
+ !pet->HasByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED) ||
+ pet->GetOwnerGUID() != _player->GetGUID() || !pet->GetCharmInfo())
+ return;
+
+ PetNameInvalidReason res = ObjectMgr::CheckPetName(name);
+ if (res != PET_NAME_SUCCESS)
+ {
+ SendPetNameInvalid(res, name, NULL);
+ return;
+ }
+
+ if (sObjectMgr->IsReservedName(name))
+ {
+ SendPetNameInvalid(PET_NAME_RESERVED, name, NULL);
+ return;
+ }
+
+ pet->SetName(name);
+
+ Unit* owner = pet->GetOwner();
+ if (owner && (owner->GetTypeId() == TYPEID_PLAYER) && owner->ToPlayer()->GetGroup())
+ owner->ToPlayer()->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_NAME);
+
+ pet->RemoveByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED);
+
+ if (isdeclined)
+ {
+ for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
+ {
+ recv_data >> declinedname.name[i];
+ }
+
+ std::wstring wname;
+ Utf8toWStr(name, wname);
+ if (!ObjectMgr::CheckDeclinedNames(wname, declinedname))
+ {
+ SendPetNameInvalid(PET_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME, name, &declinedname);
+ return;
+ }
+ }
+
+ SQLTransaction trans = CharacterDatabase.BeginTransaction();
+ if (isdeclined)
+ {
+ for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
+ CharacterDatabase.EscapeString(declinedname.name[i]);
+ trans->PAppend("DELETE FROM character_pet_declinedname WHERE owner = '%u' AND id = '%u'", _player->GetGUIDLow(), pet->GetCharmInfo()->GetPetNumber());
+ trans->PAppend("INSERT INTO character_pet_declinedname (id, owner, genitive, dative, accusative, instrumental, prepositional) VALUES ('%u', '%u', '%s', '%s', '%s', '%s', '%s')",
+ pet->GetCharmInfo()->GetPetNumber(), _player->GetGUIDLow(), declinedname.name[0].c_str(), declinedname.name[1].c_str(), declinedname.name[2].c_str(), declinedname.name[3].c_str(), declinedname.name[4].c_str());
+ }
+
+ CharacterDatabase.EscapeString(name);
+ trans->PAppend("UPDATE character_pet SET name = '%s', renamed = '1' WHERE owner = '%u' AND id = '%u'", name.c_str(), _player->GetGUIDLow(), pet->GetCharmInfo()->GetPetNumber());
+ CharacterDatabase.CommitTransaction(trans);
+
+ pet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, uint32(time(NULL))); // cast can't be helped
+}
+
+void WorldSession::HandlePetAbandon(WorldPacket & recv_data)
+{
+ uint64 guid;
+ recv_data >> guid; //pet guid
+ sLog->outDetail("HandlePetAbandon. CMSG_PET_ABANDON pet guid is %u", GUID_LOPART(guid));
+
+ if (!_player->IsInWorld())
+ return;
+
+ // pet/charmed
+ Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
+ if (pet)
+ {
+ if (pet->isPet())
+ {
+ if (pet->GetGUID() == _player->GetPetGUID())
+ {
+ uint32 feelty = pet->GetPower(POWER_HAPPINESS);
+ pet->SetPower(POWER_HAPPINESS, feelty > 50000 ? (feelty-50000) : 0);
+ }
+
+ _player->RemovePet((Pet*)pet, PET_SAVE_AS_DELETED);
+ }
+ else if (pet->GetGUID() == _player->GetCharmGUID())
+ _player->StopCastingCharm();
+ }
+}
+
+void WorldSession::HandlePetSpellAutocastOpcode(WorldPacket& recvPacket)
+{
+ sLog->outDetail("CMSG_PET_SPELL_AUTOCAST");
+ uint64 guid;
+ uint32 spellid;
+ uint8 state; //1 for on, 0 for off
+ recvPacket >> guid >> spellid >> state;
+
+ if (!_player->GetGuardianPet() && !_player->GetCharm())
+ return;
+
+ if (ObjectAccessor::FindPlayer(guid))
+ return;
+
+ Creature* pet=ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
+
+ if (!pet || (pet != _player->GetGuardianPet() && pet != _player->GetCharm()))
+ {
+ sLog->outError("HandlePetSpellAutocastOpcode.Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid)), GetPlayer()->GetName());
+ return;
+ }
+
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid);
+ // do not add not learned spells/ passive spells
+ if (!pet->HasSpell(spellid) || spellInfo->IsAutocastable())
+ return;
+
+ CharmInfo* charmInfo = pet->GetCharmInfo();
+ if (!charmInfo)
+ {
+ sLog->outError("WorldSession::HandlePetSpellAutocastOpcod: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId());
+ return;
+ }
+
+ if (pet->isPet())
+ ((Pet*)pet)->ToggleAutocast(spellInfo, state);
+ else
+ pet->GetCharmInfo()->ToggleCreatureAutocast(spellInfo, state);
+
+ charmInfo->SetSpellAutocast(spellInfo, state);
+}
+
+void WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket)
+{
+ sLog->outDetail("WORLD: CMSG_PET_CAST_SPELL");
+
+ uint64 guid;
+ uint8 castCount;
+ uint32 spellId;
+ uint8 castFlags;
+
+ recvPacket >> guid >> castCount >> spellId >> castFlags;
+
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_PET_CAST_SPELL, guid: " UI64FMTD ", castCount: %u, spellId %u, castFlags %u", guid, castCount, spellId, castFlags);
+
+ // This opcode is also sent from charmed and possessed units (players and creatures)
+ if (!_player->GetGuardianPet() && !_player->GetCharm())
+ return;
+
+ Unit* caster = ObjectAccessor::GetUnit(*_player, guid);
+
+ if (!caster || (caster != _player->GetGuardianPet() && caster != _player->GetCharm()))
+ {
+ sLog->outError("HandlePetCastSpellOpcode: Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid)), GetPlayer()->GetName());
+ return;
+ }
+
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
+ if (!spellInfo)
+ {
+ sLog->outError("WORLD: unknown PET spell id %i", spellId);
+ return;
+ }
+
+ if (spellInfo->StartRecoveryCategory > 0) // Check if spell is affected by GCD
+ if (caster->GetTypeId() == TYPEID_UNIT && caster->GetCharmInfo() && caster->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo))
+ {
+ caster->SendPetCastFail(spellId, SPELL_FAILED_NOT_READY);
+ return;
+ }
+
+ // do not cast not learned spells
+ if (!caster->HasSpell(spellId) || spellInfo->IsPassive())
+ return;
+
+ SpellCastTargets targets;
+ targets.Read(recvPacket, caster);
+ HandleClientCastFlags(recvPacket, castFlags, targets);
+
+ caster->ClearUnitState(UNIT_STAT_FOLLOW);
+
+ Spell* spell = new Spell(caster, spellInfo, TRIGGERED_NONE);
+ spell->m_cast_count = castCount; // probably pending spell cast
+ spell->m_targets = targets;
+
+ // TODO: need to check victim?
+ SpellCastResult result;
+ if (caster->m_movedPlayer)
+ result = spell->CheckPetCast(caster->m_movedPlayer->GetSelectedUnit());
+ else
+ result = spell->CheckPetCast(NULL);
+ if (result == SPELL_CAST_OK)
+ {
+ if (caster->GetTypeId() == TYPEID_UNIT)
+ {
+ Creature* pet = caster->ToCreature();
+ pet->AddCreatureSpellCooldown(spellId);
+ if (pet->isPet())
+ {
+ Pet* p = (Pet*)pet;
+ // 10% chance to play special pet attack talk, else growl
+ // actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
+ if (p->getPetType() == SUMMON_PET && (urand(0, 100) < 10))
+ pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL);
+ else
+ pet->SendPetAIReaction(guid);
+ }
+ }
+
+ spell->prepare(&(spell->m_targets));
+ }
+ else
+ {
+ caster->SendPetCastFail(spellId, result);
+ if (caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ if (!caster->ToPlayer()->HasSpellCooldown(spellId))
+ GetPlayer()->SendClearCooldown(spellId, caster);
+ }
+ else
+ {
+ if (!caster->ToCreature()->HasSpellCooldown(spellId))
+ GetPlayer()->SendClearCooldown(spellId, caster);
+ }
+
+ spell->finish(false);
+ delete spell;
+ }
+}
+
+void WorldSession::SendPetNameInvalid(uint32 error, const std::string& name, DeclinedName *declinedName)
+{
+ WorldPacket data(SMSG_PET_NAME_INVALID, 4 + name.size() + 1 + 1);
+ data << uint32(error);
+ data << name;
+ if (declinedName)
+ {
+ data << uint8(1);
+ for (uint32 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
+ data << declinedName->name[i];
+ }
+ else
+ data << uint8(0);
+ SendPacket(&data);
+}
+
+void WorldSession::HandlePetLearnTalent(WorldPacket & recv_data)
+{
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_PET_LEARN_TALENT");
+
+ uint64 guid;
+ uint32 talent_id, requested_rank;
+ recv_data >> guid >> talent_id >> requested_rank;
+
+ _player->LearnPetTalent(guid, talent_id, requested_rank);
+ _player->SendTalentsInfoData(true);
+}
+
+void WorldSession::HandleLearnPreviewTalentsPet(WorldPacket & recv_data)
+{
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LEARN_PREVIEW_TALENTS_PET");
+
+ uint64 guid;
+ recv_data >> guid;
+
+ uint32 talentsCount;
+ recv_data >> talentsCount;
+
+ uint32 talentId, talentRank;
+
+ for (uint32 i = 0; i < talentsCount; ++i)
+ {
+ recv_data >> talentId >> talentRank;
+
+ _player->LearnPetTalent(guid, talentId, talentRank);
+ }
+
+ _player->SendTalentsInfoData(true);
+}