diff options
Diffstat (limited to 'src/game/PetHandler.cpp')
-rw-r--r-- | src/game/PetHandler.cpp | 379 |
1 files changed, 201 insertions, 178 deletions
diff --git a/src/game/PetHandler.cpp b/src/game/PetHandler.cpp index 8ffe2529227..3dc7c6d4ff1 100644 --- a/src/game/PetHandler.cpp +++ b/src/game/PetHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 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 @@ -21,18 +21,16 @@ #include "Common.h" #include "WorldPacket.h" #include "WorldSession.h" -#include "World.h" #include "ObjectMgr.h" #include "SpellMgr.h" #include "Log.h" #include "Opcodes.h" #include "Spell.h" #include "ObjectAccessor.h" -#include "MapManager.h" #include "CreatureAI.h" #include "Util.h" #include "Pet.h" -#include "Language.h" +#include "World.h" void WorldSession::HandlePetAction( WorldPacket & recv_data ) { @@ -49,39 +47,49 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) // used also for charmed creature Unit* pet= ObjectAccessor::GetUnit(*_player, guid1); - sLog.outDetail("HandlePetAction.Pet %u flag is %u, spellid is %u, target %u.\n", uint32(GUID_LOPART(guid1)), flag, spellid, uint32(GUID_LOPART(guid2)) ); + sLog.outDetail("HandlePetAction.Pet %u flag is %u, spellid is %u, target %u.", uint32(GUID_LOPART(guid1)), flag, spellid, uint32(GUID_LOPART(guid2)) ); if(!pet) { - sLog.outError( "Pet %u not exist.\n", uint32(GUID_LOPART(guid1)) ); + sLog.outError( "Pet %u not exist.", uint32(GUID_LOPART(guid1)) ); return; } - if(pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharm()) + if(pet != GetPlayer()->GetFirstControlled()) { - sLog.outError("HandlePetAction.Pet %u isn't pet of player %s.\n", uint32(GUID_LOPART(guid1)), GetPlayer()->GetName() ); + 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 "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID()); + 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: //0x0700 - // Possessed or shared vision pets are only able to attack - if ((pet->isPossessed() || pet->HasAuraType(SPELL_AURA_BIND_SIGHT)) && spellid != COMMAND_ATTACK) - return; - switch(spellid) { case COMMAND_STAY: //flat=1792 //STAY @@ -124,45 +132,58 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) } pet->clearUnitState(UNIT_STAT_FOLLOW); - - if(pet->GetTypeId() != TYPEID_PLAYER && ((Creature*)pet)->IsAIEnabled) + // This is true if pet has no target or has target but targets differs. + if(pet->getVictim() != TargetUnit) { - ((Creature*)pet)->AI()->AttackStart(TargetUnit); + if (pet->getVictim()) + pet->AttackStop(); - //10% chance to play special pet attack talk, else growl - if(((Creature*)pet)->isPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != TargetUnit && urand(0, 100) < 10) - pet->SendPetTalk((uint32)PET_TALK_ATTACK); - else + if(pet->GetTypeId() != TYPEID_PLAYER && ((Creature*)pet)->IsAIEnabled) { - // 90% chance for pet and 100% chance for charmed creature - pet->SendPetAIReaction(guid1); + ((Creature*)pet)->AI()->AttackStart(TargetUnit); + + //10% chance to play special pet attack talk, else growl + if(((Creature*)pet)->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(); + else // charmed player + { + if(pet->getVictim() && pet->getVictim() != TargetUnit) + pet->AttackStop(); - pet->Attack(TargetUnit,true); - pet->SendPetAIReaction(guid1); + pet->Attack(TargetUnit,true); + pet->SendPetAIReaction(guid1); + } } break; } case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet) - if(((Creature*)pet)->isPet()) + if(pet->GetCharmerGUID() == GetPlayer()->GetGUID()) + _player->StopCastingCharm(); + else if(pet->GetOwnerGUID() == GetPlayer()->GetGUID()) { - Pet* p = (Pet*)pet; - if(p->getPetType() == HUNTER_PET) - _player->RemovePet(p,PET_SAVE_AS_DELETED); - else - //dismissing a summoned pet is like killing them (this prevents returning a soulshard...) - p->setDeathState(CORPSE); + assert(pet->GetTypeId() == TYPEID_UNIT); + if(((Creature*)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(((Creature*)pet)->HasSummonMask(SUMMON_MASK_MINION)) + { + ((Minion*)pet)->UnSummon(); + } } - else // charmed or possessed - _player->Uncharm(); break; default: - sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.\n", flag, spellid); + sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", flag, spellid); } break; case ACT_REACTION: // 0x600 @@ -176,28 +197,26 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) break; } break; - case ACT_DISABLED: //0x8100 spell (disabled), ignore - case ACT_CAST: //0x0100 - case ACT_ENABLED: //0xc100 spell + case ACT_DISABLED: // 0x8100 spell (disabled), ignore + case ACT_PASSIVE: // 0x0100 + case ACT_ENABLED: // 0xC100 spell { - Unit* unit_target; - if(guid2) - unit_target = ObjectAccessor::GetUnit(*_player,guid2); - else - unit_target = NULL; - + Unit* unit_target = NULL; if (((Creature*)pet)->GetGlobalCooldown() > 0) return; + 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\n", spellid); + sLog.outError("WORLD: unknown PET spell id %i", spellid); return; } - for(uint32 i = 0; i < 3;i++) + 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; @@ -211,25 +230,32 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) Spell *spell = new Spell(pet, spellInfo, false); - int16 result = spell->PetCanCast(unit_target); + SpellCastResult result = spell->CheckPetCast(unit_target); - //auto turn to target unless possessed + //auto turn to target unless possessed if(result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->isPossessed()) { - pet->SetInFront(unit_target); - if( unit_target->GetTypeId() == TYPEID_PLAYER ) - pet->SendUpdateToPlayer( (Player*)unit_target ); - if(Unit* powner = pet->GetCharmerOrOwner()) + 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((Player*)powner); - result = -1; + result = SPELL_CAST_OK; } - if(result == -1) + if(result == SPELL_CAST_OK) { ((Creature*)pet)->AddCreatureSpellCooldown(spellid); - if (((Creature*)pet)->isPet()) - ((Pet*)pet)->CheckLearning(spellid); unit_target = spell->m_targets.getUnitTarget(); @@ -244,12 +270,15 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) if( unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->isPossessed()) { - pet->clearUnitState(UNIT_STAT_FOLLOW); - if(pet->getVictim()) - pet->AttackStop(); - pet->GetMotionMaster()->Clear(); - if (((Creature*)pet)->IsAIEnabled) - ((Creature*)pet)->AI()->AttackStart(unit_target); + // 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 (((Creature*)pet)->IsAIEnabled) + ((Creature*)pet)->AI()->AttackStart(unit_target); + } } spell->prepare(&(spell->m_targets)); @@ -257,25 +286,12 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) else { if(pet->isPossessed()) - { - WorldPacket data(SMSG_CAST_FAILED, (4+1+1)); - data << uint32(spellid) << uint8(2) << uint8(result); - switch (result) - { - case SPELL_FAILED_REQUIRES_SPELL_FOCUS: - data << uint32(spellInfo->RequiresSpellFocus); - break; - case SPELL_FAILED_REQUIRES_AREA: - data << uint32(spellInfo->AreaId); - break; - } - SendPacket(&data); - } + Spell::SendCastResult(GetPlayer(),spellInfo,0,result); else pet->SendPetCastFail(spellid, result); if(!((Creature*)pet)->HasSpellCooldown(spellid)) - pet->SendPetClearCooldown(spellid); + GetPlayer()->SendClearCooldown(spellid, pet); spell->finish(false); delete spell; @@ -283,7 +299,7 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) break; } default: - sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.\n", flag, spellid); + sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", flag, spellid); } } @@ -291,7 +307,7 @@ void WorldSession::HandlePetNameQuery( WorldPacket & recv_data ) { CHECK_PACKET_SIZE(recv_data,4+8); - sLog.outDetail( "HandlePetNameQuery. CMSG_PET_NAME_QUERY\n" ); + sLog.outDetail( "HandlePetNameQuery. CMSG_PET_NAME_QUERY" ); uint32 petnumber; uint64 petguid; @@ -304,7 +320,7 @@ void WorldSession::HandlePetNameQuery( WorldPacket & recv_data ) void WorldSession::SendPetNameQuery( uint64 petguid, uint32 petnumber) { - Creature* pet = ObjectAccessor::GetCreatureOrPet(*_player, petguid); + Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, petguid); if(!pet || !pet->GetCharmInfo() || pet->GetCharmInfo()->GetPetNumber() != petnumber) return; @@ -318,7 +334,7 @@ void WorldSession::SendPetNameQuery( uint64 petguid, uint32 petnumber) if( pet->isPet() && ((Pet*)pet)->GetDeclinedNames() ) { data << uint8(1); - for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) + for(uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i) data << ((Pet*)pet)->GetDeclinedNames()->name[i]; } else @@ -331,7 +347,7 @@ void WorldSession::HandlePetSetAction( WorldPacket & recv_data ) { CHECK_PACKET_SIZE(recv_data, 8+4+2+2); - sLog.outDetail( "HandlePetSetAction. CMSG_PET_SET_ACTION\n" ); + sLog.outDetail( "HandlePetSetAction. CMSG_PET_SET_ACTION" ); uint64 petguid; uint32 position; @@ -341,57 +357,56 @@ void WorldSession::HandlePetSetAction( WorldPacket & recv_data ) recv_data >> petguid; - // FIXME: charmed case - //Pet* pet = ObjectAccessor::Instance().GetPet(petguid); - if(ObjectAccessor::FindPlayer(petguid)) - return; - - Creature* pet = ObjectAccessor::GetCreatureOrPet(*_player, petguid); + Unit* pet = ObjectAccessor::GetUnit(*_player, petguid); - if(!pet || (pet != _player->GetPet() && pet != _player->GetCharm())) + if(!pet || pet != _player->GetFirstControlled()) { - sLog.outError( "HandlePetSetAction: Unknown pet or pet owner.\n" ); + sLog.outError( "HandlePetSetAction: Unknown pet or pet owner." ); return; } CharmInfo *charmInfo = pet->GetCharmInfo(); if(!charmInfo) { - sLog.outError("WorldSession::HandlePetSetAction: object "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID()); + 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; - for(uint8 i = 0; i < count; i++) + for(uint8 i = 0; i < count; ++i) { recv_data >> position; recv_data >> spell_id; recv_data >> act_state; - sLog.outDetail( "Player %s has changed pet spell action. Position: %u, Spell: %u, State: 0x%X\n", _player->GetName(), position, spell_id, act_state); + sLog.outDetail( "Player %s has changed pet spell action. Position: %u, Spell: %u, State: 0x%X", _player->GetName(), position, spell_id, act_state); + + //ignore invalid position + if(position >= MAX_UNIT_ACTION_BAR_INDEX) + return; - //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_CAST) && spell_id && !pet->HasSpell(spell_id))) + //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->isCharmed()) - charmInfo->ToggleCreatureAutocast(spell_id, true); - else + if(pet->GetTypeId() == TYPEID_UNIT && ((Creature*)pet)->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->isCharmed()) - charmInfo->ToggleCreatureAutocast(spell_id, false); - else + if(pet->GetTypeId() == TYPEID_UNIT && ((Creature*)pet)->isPet()) ((Pet*)pet)->ToggleAutocast(spell_id, false); + else + charmInfo->ToggleCreatureAutocast(spell_id, false); + } - charmInfo->GetActionBarEntry(position)->Type = act_state; - charmInfo->GetActionBarEntry(position)->SpellOrAction = spell_id; + charmInfo->SetActionBar(position,spell_id,ActiveStates(act_state)); } } } @@ -400,7 +415,7 @@ void WorldSession::HandlePetRename( WorldPacket & recv_data ) { CHECK_PACKET_SIZE(recv_data, 8+1); - sLog.outDetail( "HandlePetRename. CMSG_PET_RENAME\n" ); + sLog.outDetail( "HandlePetRename. CMSG_PET_RENAME" ); uint64 petguid; uint8 isdeclined; @@ -442,7 +457,7 @@ void WorldSession::HandlePetRename( WorldPacket & recv_data ) if(isdeclined) { - for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) + for(uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i) { CHECK_PACKET_SIZE(recv_data, recv_data.rpos() + 1); recv_data >> declinedname.name[i]; @@ -460,7 +475,7 @@ void WorldSession::HandlePetRename( WorldPacket & recv_data ) CharacterDatabase.BeginTransaction(); if(isdeclined) { - for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) + 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')", @@ -483,7 +498,7 @@ void WorldSession::HandlePetAbandon( WorldPacket & recv_data ) sLog.outDetail( "HandlePetAbandon. CMSG_PET_ABANDON pet guid is %u", GUID_LOPART(guid) ); // pet/charmed - Creature* pet = ObjectAccessor::GetCreatureOrPet(*_player, guid); + Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid); if(pet) { if(pet->isPet()) @@ -497,7 +512,7 @@ void WorldSession::HandlePetAbandon( WorldPacket & recv_data ) _player->RemovePet((Pet*)pet,PET_SAVE_AS_DELETED); } else if(pet->GetGUID() == _player->GetCharmGUID()) - _player->Uncharm(); + _player->StopCastingCharm(); } } @@ -507,57 +522,27 @@ void WorldSession::HandlePetUnlearnOpcode(WorldPacket& recvPacket) sLog.outDetail("CMSG_PET_UNLEARN"); uint64 guid; - recvPacket >> guid; + recvPacket >> guid; // Pet guid Pet* pet = _player->GetPet(); - if(!pet || pet->getPetType() != HUNTER_PET || pet->m_spells.size() <= 1) + if(!pet || pet->getPetType() != HUNTER_PET || pet->m_usedTalentCount == 0) return; if(guid != pet->GetGUID()) { - sLog.outError( "HandlePetUnlearnOpcode.Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); + sLog.outError( "HandlePetUnlearnOpcode.Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); return; } CharmInfo *charmInfo = pet->GetCharmInfo(); if(!charmInfo) { - sLog.outError("WorldSession::HandlePetUnlearnOpcode: object "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID()); + sLog.outError("WorldSession::HandlePetUnlearnOpcode: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId()); return; } - - uint32 cost = pet->resetTalentsCost(); - - if (GetPlayer()->GetMoney() < cost) - { - GetPlayer()->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0); - return; - } - - for(PetSpellMap::iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end();) - { - uint32 spell_id = itr->first; // Pet::removeSpell can invalidate iterator at erase NEW spell - ++itr; - pet->removeSpell(spell_id); - } - - pet->SetTP(pet->getLevel() * (pet->GetLoyaltyLevel() - 1)); - - for(uint8 i = 0; i < 10; i++) - { - if(charmInfo->GetActionBarEntry(i)->SpellOrAction && charmInfo->GetActionBarEntry(i)->Type == ACT_ENABLED || charmInfo->GetActionBarEntry(i)->Type == ACT_DISABLED) - charmInfo->GetActionBarEntry(i)->SpellOrAction = 0; - } - - // relearn pet passives - pet->LearnPetPassives(); - - pet->m_resetTalentsTime = time(NULL); - pet->m_resetTalentsCost = cost; - GetPlayer()->ModifyMoney(-(int32)cost); - - GetPlayer()->PetSpellInitialize(); + pet->resetTalents(); + _player->SendTalentsInfoData(true); } void WorldSession::HandlePetSpellAutocastOpcode( WorldPacket& recvPacket ) @@ -571,63 +556,62 @@ void WorldSession::HandlePetSpellAutocastOpcode( WorldPacket& recvPacket ) uint8 state; //1 for on, 0 for off recvPacket >> guid >> spellid >> spellid2 >> state; - if(!_player->GetPet() && !_player->GetCharm()) + if(!_player->GetGuardianPet() && !_player->GetCharm()) return; if(ObjectAccessor::FindPlayer(guid)) return; - Creature* pet=ObjectAccessor::GetCreatureOrPet(*_player,guid); + Creature* pet=ObjectAccessor::GetCreatureOrPetOrVehicle(*_player,guid); - if(!pet || (pet != _player->GetPet() && pet != _player->GetCharm())) + if(!pet || (pet != _player->GetGuardianPet() && pet != _player->GetCharm())) { - sLog.outError( "HandlePetSpellAutocastOpcode.Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); + 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) || IsPassiveSpell(spellid)) + if(!pet->HasSpell(spellid) || IsAutocastableSpell(spellid)) return; CharmInfo *charmInfo = pet->GetCharmInfo(); if(!charmInfo) { - sLog.outError("WorldSession::HandlePetSpellAutocastOpcod: object "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID()); + 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->isCharmed()) - //state can be used as boolean - pet->GetCharmInfo()->ToggleCreatureAutocast(spellid, state); - else + if(pet->isPet()) ((Pet*)pet)->ToggleAutocast(spellid, state); + else + pet->GetCharmInfo()->ToggleCreatureAutocast(spellid, state); - for(uint8 i = 0; i < 10; ++i) - { - if((charmInfo->GetActionBarEntry(i)->Type == ACT_ENABLED || charmInfo->GetActionBarEntry(i)->Type == ACT_DISABLED) && spellid == charmInfo->GetActionBarEntry(i)->SpellOrAction) - charmInfo->GetActionBarEntry(i)->Type = state ? ACT_ENABLED : ACT_DISABLED; - } + charmInfo->SetSpellAutocast(spellid,state); } void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) { sLog.outDetail("WORLD: CMSG_PET_CAST_SPELL"); - CHECK_PACKET_SIZE(recvPacket,8+4); + CHECK_PACKET_SIZE(recvPacket,8+1+4+1); uint64 guid; uint32 spellid; + uint8 cast_count; + uint8 unk_flags; // flags (if 0x02 - some additional data are received) - recvPacket >> guid >> spellid; + 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->GetPet() && !_player->GetCharm()) + if(!_player->GetGuardianPet() && !_player->GetCharm()) return; Unit* caster = ObjectAccessor::GetUnit(*_player, guid); - if(!caster || (caster != _player->GetPet() && caster != _player->GetCharm())) + if(!caster || (caster != _player->GetGuardianPet() && caster != _player->GetCharm())) { - sLog.outError( "HandlePetCastSpellOpcode: Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); + sLog.outError( "HandlePetCastSpellOpcode: Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); return; } @@ -640,7 +624,7 @@ void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid); if(!spellInfo) { - sLog.outError("WORLD: unknown PET spell id %i\n", spellid); + sLog.outError("WORLD: unknown PET spell id %i", spellid); return; } @@ -655,10 +639,11 @@ void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) 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; - int16 result = spell->PetCanCast(NULL); - if(result == -1) + SpellCastResult result = spell->CheckPetCast(NULL); + if(result == SPELL_CAST_OK) { if(caster->GetTypeId() == TYPEID_UNIT) { @@ -667,7 +652,6 @@ void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) if(pet->isPet()) { Pet* p = (Pet*)pet; - p->CheckLearning(spellid); // 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)) @@ -685,12 +669,12 @@ void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) if(caster->GetTypeId() == TYPEID_PLAYER) { if(!((Player*)caster)->HasSpellCooldown(spellid)) - caster->SendPetClearCooldown(spellid); + GetPlayer()->SendClearCooldown(spellid, caster); } else { if(!((Creature*)caster)->HasSpellCooldown(spellid)) - caster->SendPetClearCooldown(spellid); + GetPlayer()->SendClearCooldown(spellid, caster); } spell->finish(false); @@ -714,3 +698,42 @@ void WorldSession::SendPetNameInvalid(uint32 error, const std::string& name, Dec SendPacket(&data); } +void WorldSession::HandlePetLearnTalent( WorldPacket & recv_data ) +{ + sLog.outDebug("WORLD: CMSG_PET_LEARN_TALENT"); + + CHECK_PACKET_SIZE(recv_data, 8+4+4); + + 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"); + + CHECK_PACKET_SIZE(recv_data, 8+4); + + uint64 guid; + recv_data >> guid; + + uint32 talentsCount; + recv_data >> talentsCount; + + uint32 talentId, talentRank; + + for(uint32 i = 0; i < talentsCount; ++i) + { + CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4+4); + + recv_data >> talentId >> talentRank; + + _player->LearnPetTalent(guid, talentId, talentRank); + } + + _player->SendTalentsInfoData(true); +} |