aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/PetHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/game/PetHandler.cpp')
-rw-r--r--src/server/game/PetHandler.cpp813
1 files changed, 813 insertions, 0 deletions
diff --git a/src/server/game/PetHandler.cpp b/src/server/game/PetHandler.cpp
new file mode 100644
index 00000000000..34e6845762b
--- /dev/null
+++ b/src/server/game/PetHandler.cpp
@@ -0,0 +1,813 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#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"
+
+void WorldSession::HandleDismissCritter(WorldPacket &recv_data)
+{
+ uint64 guid;
+ recv_data >> guid;
+
+ sLog.outDebug("WORLD: Received CMSG_DISMISS_CRITTER for GUID %u", guid);
+
+ Unit* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
+
+ if (!pet)
+ {
+ sLog.outError("Vanitypet %u does not exist", uint32(GUID_LOPART(guid)));
+ 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 is %u, spellid is %u, target %u.", uint32(GUID_LOPART(guid1)), uint32(flag), spellid, uint32(GUID_LOPART(guid2)));
+ if (!pet)
+ {
+ sLog.outError("Pet %u not exist.", uint32(GUID_LOPART(guid1)));
+ return;
+ }
+
+ if (pet != GetPlayer()->GetFirstControlled())
+ {
+ sLog.outError("HandlePetAction.Pet %u isn't pet of player %s.", uint32(GUID_LOPART(guid1)), GetPlayer()->GetName());
+ return;
+ }
+
+ if (!pet->isAlive())
+ 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::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid, uint16 flag, uint64 guid2)
+{
+ CharmInfo *charmInfo = pet->GetCharmInfo();
+ if (!charmInfo)
+ {
+ sLog.outError("WorldSession::HandlePetAction: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", 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()->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 (!pet->canAttack(TargetUnit))
+ return;
+
+ // Not let attack through obstructions
+ if (sWorld.getConfig(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
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid);
+ if (!spellInfo)
+ {
+ sLog.outError("WORLD: unknown PET spell id %i", spellid);
+ return;
+ }
+
+ if (spellInfo->StartRecoveryCategory > 0)
+ if (pet->ToCreature()->GetGlobalCooldown() > 0)
+ return;
+
+ for (uint32 i = 0; i < 3; ++i)
+ {
+ if (spellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_AREA_ENEMY_SRC || spellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_AREA_ENEMY_DST || spellInfo->EffectImplicitTargetA[i] == TARGET_DEST_DYNOBJ_ENEMY)
+ return;
+ }
+
+ // do not cast not learned spells
+ if (!pet->HasSpell(spellid) || IsPassiveSpell(spellid))
+ 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, false);
+
+ 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+4+7+1));
+ data << uint32(petnumber);
+ data << "Unknown";
+ 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);
+}
+
+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 or pet owner.");
+ 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)))
+ {
+ //sign for autocast
+ if (act_state == ACT_ENABLED && spell_id)
+ {
+ if (pet->GetTypeId() == TYPEID_UNIT && pet->ToCreature()->isPet())
+ ((Pet*)pet)->ToggleAutocast(spell_id, true);
+ else
+ charmInfo->ToggleCreatureAutocast(spell_id, true);
+ }
+ //sign for no/turn off autocast
+ else if (act_state == ACT_DISABLED && spell_id)
+ {
+ if (pet->GetTypeId() == TYPEID_UNIT && pet->ToCreature()->isPet())
+ ((Pet*)pet)->ToggleAutocast(spell_id, false);
+ else
+ charmInfo->ToggleCreatureAutocast(spell_id, 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::GetPet(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 (objmgr.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(GetMainPartOfName(wname,0),declinedname))
+ {
+ SendPetNameInvalid(PET_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME, name, &declinedname);
+ return;
+ }
+ }
+
+ CharacterDatabase.BeginTransaction();
+ if (isdeclined)
+ {
+ for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
+ CharacterDatabase.escape_string(declinedname.name[i]);
+ CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE owner = '%u' AND id = '%u'", _player->GetGUIDLow(), pet->GetCharmInfo()->GetPetNumber());
+ CharacterDatabase.PExecute("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.escape_string(name);
+ CharacterDatabase.PExecute("UPDATE character_pet SET name = '%s', renamed = '1' WHERE owner = '%u' AND id = '%u'", name.c_str(), _player->GetGUIDLow(), pet->GetCharmInfo()->GetPetNumber());
+ CharacterDatabase.CommitTransaction();
+
+ pet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL));
+}
+
+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) > 0 ?(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;
+ }
+
+ // do not add not learned spells/ passive spells
+ if (!pet->HasSpell(spellid) || IsAutocastableSpell(spellid))
+ 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(spellid, state);
+ else
+ pet->GetCharmInfo()->ToggleCreatureAutocast(spellid, state);
+
+ charmInfo->SetSpellAutocast(spellid,state);
+}
+
+void WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket)
+{
+ sLog.outDetail("WORLD: CMSG_PET_CAST_SPELL");
+
+ uint64 guid;
+ uint32 spellid;
+ uint8 cast_count;
+ uint8 unk_flags; // flags (if 0x02 - some additional data are received)
+
+ recvPacket >> guid >> cast_count >> spellid >> unk_flags;
+
+ sLog.outDebug("WORLD: CMSG_PET_CAST_SPELL, cast_count: %u, spellid %u, unk_flags %u", cast_count, spellid, unk_flags);
+
+ // 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;
+ }
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(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->ToCreature()->GetGlobalCooldown() > 0)
+ {
+ caster->SendPetCastFail(spellid, SPELL_FAILED_NOT_READY);
+ return;
+ }
+
+ // do not cast not learned spells
+ if (!caster->HasSpell(spellid) || IsPassiveSpell(spellid))
+ return;
+
+ SpellCastTargets targets;
+ if (!targets.read(&recvPacket,caster))
+ return;
+
+ caster->clearUnitState(UNIT_STAT_FOLLOW);
+
+ Spell *spell = new Spell(caster, spellInfo, spellid == 33395); // water elemental can cast freeze as triggered
+ spell->m_cast_count = spellid == 33395 ? 0 : cast_count; // 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("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("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);
+}