void WorldSession::HandlePetCastSpellOpcode(WorldPackets::Spells::PetCastSpell& petCastSpell) { SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(petCastSpell.Cast.SpellID); if (!spellInfo) { TC_LOG_ERROR("network", "WORLD: unknown PET spell id %i", petCastSpell.Cast.SpellID); return; } // This opcode is also sent from charmed and possessed units (players and creatures) if (!_player->GetGuardianPet() && !_player->GetCharm()) return; Unit* caster = ObjectAccessor::GetUnit(*_player, petCastSpell.PetGUID); if (!caster || (caster != _player->GetGuardianPet() && caster != _player->GetCharm())) { TC_LOG_ERROR("network", "HandlePetCastSpellOpcode: %s isn't pet of player %s (%s).", petCastSpell.PetGUID.ToString().c_str(), GetPlayer()->GetName().c_str(), GetPlayer()->GetGUID().ToString().c_str()); return; } // do not cast not learned spells if (!caster->HasSpell(spellInfo->Id) || spellInfo->IsPassive()) return; SpellCastTargets targets(caster, petCastSpell.Cast); caster->ClearUnitState(UNIT_STATE_FOLLOW); Spell* spell = new Spell(caster, spellInfo, TRIGGERED_NONE); spell->m_cast_count = petCastSpell.Cast.CastID; spell->m_misc.Data = petCastSpell.Cast.Misc; spell->m_targets = targets; SpellCastResult result = spell->CheckPetCast(NULL); if (result == SPELL_CAST_OK) { if (Creature* creature = caster->ToCreature()) { if (Pet* pet = creature->ToPet()) { // 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->getPetType() == SUMMON_PET && (urand(0, 100) < 10)) pet->SendPetTalk(PET_TALK_SPECIAL_SPELL); else pet->SendPetAIReaction(petCastSpell.PetGUID); } } spell->prepare(&targets); } else { spell->SendPetCastResult(result); if (!caster->GetSpellHistory()->HasCooldown(spellInfo->Id)) caster->GetSpellHistory()->ResetCooldown(spellInfo->Id, true); spell->finish(false); delete spell; } }
/* void WorldSession::HandleDismissCritter(WorldPacket& recvData) { uint64 guid; recvData >> guid; IC_LOG_DEBUG("network", "WORLD: Received CMSG_DISMISS_CRITTER for GUID " UI64FMTD, guid); Unit* pet = ObjectAccessor::GetCreatureOrPet(*_player, guid); if (!pet) { IC_LOG_DEBUG("network", "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().c_str(), GetPlayer()->GetGUIDLow(), GetAccountId()); return; } if (_player->GetCritterGUID() == pet->GetGUID()) { if (pet->GetTypeId() == TYPEID_UNIT && pet->ToCreature()->IsSummon()) pet->ToTempSummon()->UnSummon(); } } */ void WorldSession::HandlePetAction(WorldPacket& recvData) { uint64 guid1; uint32 data; uint64 guid2; recvData >> guid1; //pet guid recvData >> data; recvData >> 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); IC_LOG_INFO("network", "HandlePetAction: Pet %u - flag: %u, spellid: %u, target: %u.", uint32(GUID_LOPART(guid1)), uint32(flag), spellid, uint32(GUID_LOPART(guid2))); if (!pet) { IC_LOG_ERROR("network", "HandlePetAction: Pet (GUID: %u) doesn't exist for player %s (GUID: %u)", uint32(GUID_LOPART(guid1)), GetPlayer()->GetName().c_str(), GUID_LOPART(GetPlayer()->GetGUID())); return; } if (pet != GetPlayer()->GetFirstControlled()) { IC_LOG_ERROR("network", "HandlePetAction: Pet (GUID: %u) does not belong to player %s (GUID: %u)", uint32(GUID_LOPART(guid1)), GetPlayer()->GetName().c_str(), GUID_LOPART(GetPlayer()->GetGUID())); 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 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); CharmInfo* charmInfo = pet->GetCharmInfo(); if (!charmInfo) { IC_LOG_ERROR("network", "WorldSession::HandlePetAction(petGuid: " UI64FMTD ", tagGuid: " UI64FMTD ", spellId: %u, flag: %u): object (GUID: %u Entry: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", guid1, guid2, spellid, flag, pet->GetGUIDLow(), pet->GetEntry(), pet->GetTypeId()); return; } switch (flag) { case ACT_COMMAND: //0x07 switch (spellid) { case COMMAND_STAY: //flat=1792 //STAY pet->StopMoving(); pet->GetMotionMaster()->Clear(false); pet->GetMotionMaster()->MoveIdle(); charmInfo->SetCommandState(COMMAND_STAY); charmInfo->SetIsCommandAttack(false); charmInfo->SetIsAtStay(true); charmInfo->SetIsCommandFollow(false); 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->SetIsCommandFollow(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; pet->ClearUnitState(UNIT_STATE_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->SetIsCommandFollow(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->SetIsCommandFollow(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: IC_LOG_ERROR("network", "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) { IC_LOG_ERROR("network", "WORLD: unknown PET spell id %i", spellid); 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()) { if (unit_target) { pet->SetInFront(unit_target); if (Player* player = unit_target->ToPlayer()) pet->SendUpdateToPlayer(player); } else if (Unit* unit_target2 = spell->m_targets.GetUnitTarget()) { pet->SetInFront(unit_target2); if (Player* player = unit_target2->ToPlayer()) pet->SendUpdateToPlayer(player); } if (Unit* powner = pet->GetCharmerOrOwner()) if (Player* player = powner->ToPlayer()) pet->SendUpdateToPlayer(player); 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()) { // 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()) /// @todo: confirm this check Spell::SendCastResult(GetPlayer(), spellInfo, 0, result); else spell->SendPetCastResult(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: IC_LOG_ERROR("network", "WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid); } }
void WorldSession::HandlePetAction(WorldPacket& recv_data) { ObjectGuid petGuid; uint32 data; ObjectGuid targetGuid; recv_data >> petGuid; recv_data >> data; recv_data >> targetGuid; uint32 spellid = UNIT_ACTION_BUTTON_ACTION(data); uint8 flag = UNIT_ACTION_BUTTON_TYPE(data); // delete = 0x07 CastSpell = C1 DETAIL_LOG("HandlePetAction: %s flag is %u, spellid is %u, target %s.", petGuid.GetString().c_str(), uint32(flag), spellid, targetGuid.GetString().c_str()); // used also for charmed creature/player Unit* pet = _player->GetMap()->GetUnit(petGuid); if (!pet) { sLog.outError("HandlePetAction: %s not exist.", petGuid.GetString().c_str()); return; } if (GetPlayer()->GetObjectGuid() != pet->GetCharmerOrOwnerGuid()) { sLog.outError("HandlePetAction: %s isn't controlled by %s.", petGuid.GetString().c_str(), GetPlayer()->GetGuidStr().c_str()); return; } if (!pet->isAlive()) return; if (pet->GetTypeId() == TYPEID_PLAYER) { // controller player can only do melee attack if (!(flag == ACT_COMMAND && spellid == COMMAND_ATTACK)) return; } else if (((Creature*)pet)->IsPet()) { // pet can have action bar disabled if (((Pet*)pet)->GetModeFlags() & PET_MODE_DISABLE_ACTIONS) return; } 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->StopMoving(); pet->GetMotionMaster()->Clear(false); pet->GetMotionMaster()->MoveIdle(); charmInfo->SetCommandState(COMMAND_STAY); break; case COMMAND_FOLLOW: // spellid=1792 //FOLLOW pet->AttackStop(); pet->GetMotionMaster()->MoveFollow(_player, PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); charmInfo->SetCommandState(COMMAND_FOLLOW); break; case COMMAND_ATTACK: // spellid=1792 // ATTACK { Unit* TargetUnit = _player->GetMap()->GetUnit(targetGuid); if (!TargetUnit) return; // not let attack friendly units. if (GetPlayer()->IsFriendlyTo(TargetUnit)) return; // Not let attack through obstructions if (!pet->IsWithinLOSInMap(TargetUnit)) return; // This is true if pet has no target or has target but targets differs. if (pet->getVictim() != TargetUnit) { if (pet->getVictim()) pet->AttackStop(); if (pet->hasUnitState(UNIT_STAT_CONTROLLED)) { pet->Attack(TargetUnit, true); pet->SendPetAIReaction(); } else { pet->GetMotionMaster()->Clear(); if (((Creature*)pet)->AI()) ((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 && roll_chance_i(10)) pet->SendPetTalk((uint32)PET_TALK_ATTACK); else { // 90% chance for pet and 100% chance for charmed creature pet->SendPetAIReaction(); } } } break; } case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet) if (((Creature*)pet)->IsPet()) { Pet* p = (Pet*)pet; if (p->getPetType() == HUNTER_PET) p->Unsummon(PET_SAVE_AS_DELETED, _player); else // dismissing a summoned pet is like killing them (this prevents returning a soulshard...) p->SetDeathState(CORPSE); } else // charmed _player->Uncharm(); 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 case REACT_DEFENSIVE: // recovery case REACT_AGGRESSIVE: // activete charmInfo->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 (targetGuid) unit_target = _player->GetMap()->GetUnit(targetGuid); // do not cast unknown spells SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellid); if (!spellInfo) { sLog.outError("WORLD: unknown PET spell id %i", spellid); return; } if (pet->GetCharmInfo() && pet->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) return; for (int i = 0; i < MAX_EFFECT_INDEX; ++i) { if (spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA || spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA_INSTANT || spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED) return; } // do not cast not learned spells if (!pet->HasSpell(spellid) || IsPassiveSpell(spellInfo)) return; pet->clearUnitState(UNIT_STAT_MOVING); 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->HasAuraType(SPELL_AURA_MOD_POSSESS)) { if (unit_target) { pet->SetInFront(unit_target); if (unit_target->GetTypeId() == TYPEID_PLAYER) pet->SendCreateUpdateToPlayer( (Player*)unit_target ); } else if (Unit* unit_target2 = spell->m_targets.getUnitTarget()) { pet->SetInFront(unit_target2); if (unit_target2->GetTypeId() == TYPEID_PLAYER) pet->SendCreateUpdateToPlayer((Player*)unit_target2); } if (Unit* powner = pet->GetCharmerOrOwner()) if (powner->GetTypeId() == TYPEID_PLAYER) pet->SendCreateUpdateToPlayer((Player*)powner); result = SPELL_CAST_OK; } if (result == SPELL_CAST_OK) { ((Creature*)pet)->AddCreatureSpellCooldown(spellid); if (((Creature*)pet)->IsPet()) ((Pet*)pet)->CheckLearning(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 (((Creature*)pet)->IsPet() && (((Pet*)pet)->getPetType() == SUMMON_PET) && (pet != unit_target) && (urand(0, 100) < 10)) pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL); else { pet->SendPetAIReaction(); } if (unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) { // 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)->AI()) ((Creature*)pet)->AI()->AttackStart(unit_target); } } spell->prepare(&(spell->m_targets)); } else { if (pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) Spell::SendCastResult(GetPlayer(), spellInfo, 0, result); else pet->SendPetCastFail(spellid, result); if (!((Creature*)pet)->HasSpellCooldown(spellid)) GetPlayer()->SendClearCooldown(spellid, pet); spell->finish(false); delete spell; } break; } default: sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid); } }
void WorldSession::HandlePetActionHelper(Unit* pet, uint64 guid1, uint32 spellid, uint16 flag, uint64 guid2, float x, float y, float z) { CharmInfo* charmInfo = pet->GetCharmInfo(); if (!charmInfo) { TC_LOG_ERROR("network", "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->StopMoving(); 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_STATE_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(); // Summon gargoyle should attack the same target as ghoul if (Unit* owner = pet->GetOwner()) { if (owner->getClass() == CLASS_DEATH_KNIGHT) { for (Unit::ControlList::iterator itr = owner->m_Controlled.begin(); itr != owner->m_Controlled.end(); ++itr) { if ((*itr)->GetEntry() == 27829 && !(*itr)->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) { owner->AddAura(49206, TargetUnit); break; } } } } 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); if (Unit* owner = pet->GetOwner()) if (Player* playerOwner = owner->ToPlayer()) for (Unit::ControlList::iterator itr = playerOwner->m_Controlled.begin(); itr != playerOwner->m_Controlled.end(); ++itr) if ((*itr)->isGuardian() && (*itr)->IsAIEnabled) (*itr)->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()) { PetRemoveMode removeMode = (static_cast<Pet*>(pet)->getPetType() == HUNTER_PET) ? PET_REMOVE_ABANDON : PET_REMOVE_DISMISS; GetPlayer()->RemovePet(removeMode, PET_REMOVE_FLAG_RESET_CURRENT); } else if (pet->HasUnitTypeMask(UNIT_MASK_MINION)) { ((Minion*)pet)->UnSummon(); } } break; case COMMAND_MOVE_TO: pet->StopMoving(); pet->GetMotionMaster()->Clear(false); pet->GetMotionMaster()->MoveIdle(); pet->GetMotionMaster()->MovePoint(0, x, y, z); charmInfo->SetCommandState(COMMAND_MOVE_TO); charmInfo->SetIsCommandAttack(false); charmInfo->SetIsAtStay(true); charmInfo->SetIsFollowing(false); charmInfo->SetIsReturning(false); charmInfo->SaveStayPosition(); break; default: TC_LOG_ERROR("network", "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 case REACT_ASSIST: if (pet->GetTypeId() == TYPEID_UNIT) pet->ToCreature()->SetReactState(ReactStates(spellid)); break; default: 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) { TC_LOG_ERROR("network", "WORLD: unknown PET spell id %i", spellid); return; } if (spellInfo->StartRecoveryCategory > 0) if (pet->GetCharmInfo() && pet->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(pet, spellInfo)) return; for (auto const &spellEffect : spellInfo->Effects) { if (spellEffect.TargetA.GetTarget() == TARGET_UNIT_SRC_AREA_ENEMY || spellEffect.TargetA.GetTarget() == TARGET_UNIT_DEST_AREA_ENEMY || spellEffect.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, NULL, 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: TC_LOG_ERROR("network", "WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid); } }
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::HandlePetCastSpellOpcode(WorldPacket& recvPacket) { DETAIL_LOG("WORLD: CMSG_PET_CAST_SPELL"); ObjectGuid guid; uint32 spellid; uint8 cast_count; uint8 cast_flags; // flags (if 0x02 - some additional data are received) recvPacket >> guid >> cast_count >> spellid >> cast_flags; DEBUG_LOG("WORLD: CMSG_PET_CAST_SPELL, %s, cast_count: %u, spellid %u, cast_flags %u", guid.GetString().c_str(), cast_count, spellid, cast_flags); Creature* pet = _player->GetMap()->GetAnyTypeCreature(guid); if (!pet || (guid != _player->GetPetGuid() && guid != _player->GetCharmGuid())) { sLog.outError("HandlePetCastSpellOpcode: %s isn't pet of %s .", guid.GetString().c_str(), _player->GetGuidStr().c_str()); return; } SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellid); if (!spellInfo) { sLog.outError("WORLD: unknown PET spell id %i", spellid); return; } if (pet->GetCharmInfo() && pet->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) return; Aura* triggeredByAura = pet->GetTriggeredByClientAura(spellid); // do not cast not learned spells if ((!triggeredByAura && !pet->HasSpell(spellid)) || IsPassiveSpell(spellInfo)) return; SpellCastTargets targets; recvPacket >> targets.ReadForCaster(pet); targets.ReadAdditionalData(recvPacket, cast_flags); pet->clearUnitState(UNIT_STAT_MOVING); Spell* spell = new Spell(pet, spellInfo, triggeredByAura ? true : false, pet->GetObjectGuid(), triggeredByAura ? triggeredByAura->GetSpellProto() : nullptr); spell->m_cast_count = cast_count; // probably pending spell cast spell->m_targets = targets; SpellCastResult result = triggeredByAura ? SPELL_CAST_OK : spell->CheckPetCast(nullptr); if (result == SPELL_CAST_OK) { pet->AddCreatureSpellCooldown(spellid); spell->SpellStart(&(spell->m_targets), triggeredByAura); } else { Unit* owner = pet->GetCharmerOrOwner(); if (owner && owner->GetTypeId() == TYPEID_PLAYER && !triggeredByAura) Spell::SendCastResult((Player*)owner, spellInfo, 0, result, true); if (!pet->HasSpellCooldown(spellid) && !triggeredByAura) GetPlayer()->SendClearCooldown(spellid, pet); spell->finish(false); delete spell; } }
void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) { sLog.outDetail("WORLD: CMSG_PET_CAST_SPELL"); CHECK_PACKET_SIZE(recvPacket,8+4); uint64 guid; uint32 spellid; recvPacket >> guid >> spellid; if(!_player->GetPet() && !_player->GetCharm()) return; if (GUID_HIPART(guid) == HIGHGUID_PLAYER) return; Creature* pet = ObjectAccessor::GetCreatureOrPet(*_player,guid); if(!pet || (pet != _player->GetPet() && pet!= _player->GetCharm())) { sLog.outError( "HandlePetCastSpellOpcode: Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); return; } if (pet->GetGlobalCooldown() > 0) return; SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid); if(!spellInfo) { sLog.outError("WORLD: unknown PET spell id %i\n", spellid); return; } // do not cast not learned spells if(!pet->HasSpell(spellid) || IsPassiveSpell(spellid)) return; SpellCastTargets targets; if(!targets.read(&recvPacket,pet)) return; pet->clearUnitState(UNIT_STAT_FOLLOW); Spell *spell = new Spell(pet, spellInfo, false); spell->m_targets = targets; int16 result = spell->PetCanCast(NULL); if(result == -1) { pet->AddCreatureSpellCooldown(spellid); 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)) pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL); else pet->SendPetAIReaction(guid); } spell->prepare(&(spell->m_targets)); } else { pet->SendPetCastFail(spellid, result); if(!pet->HasSpellCooldown(spellid)) pet->SendPetClearCooldown(spellid); spell->finish(false); delete spell; } }
void WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket) { sLog->outDebug(LOG_FILTER_NETWORKIO, "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(LOG_FILTER_NETWORKIO, "HandlePetCastSpellOpcode: Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid)), GetPlayer()->GetName().c_str()); return; } SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo) { sLog->outError(LOG_FILTER_NETWORKIO, "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_STATE_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::HandlePetCastSpellOpcode(WorldPacket& recvPacket) { DETAIL_LOG("WORLD: CMSG_PET_CAST_SPELL"); ObjectGuid guid; uint32 spellid; uint8 cast_count; uint8 cast_flags; // flags (if 0x02 - some additional data are received) recvPacket >> guid >> cast_count >> spellid >> cast_flags; DEBUG_LOG("WORLD: CMSG_PET_CAST_SPELL, %s, cast_count: %u, spellid %u, cast_flags %u", guid.GetString().c_str(), cast_count, spellid, cast_flags); Creature* pet = _player->GetMap()->GetAnyTypeCreature(guid); if (!pet || (guid != _player->GetPetGuid() && guid != _player->GetCharmGuid())) { sLog.outError("HandlePetCastSpellOpcode: %s isn't pet of %s .", guid.GetString().c_str(), GetPlayer()->GetGuidStr().c_str()); return; } SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellid); if (!spellInfo) { sLog.outError("WORLD: unknown PET spell id %i", spellid); return; } if (pet->GetCharmInfo() && pet->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) return; Aura* triggeredByAura = pet->GetTriggeredByClientAura(spellid); // do not cast not learned spells if ((!triggeredByAura && !pet->HasSpell(spellid)) || IsPassiveSpell(spellInfo)) return; SpellCastTargets targets; recvPacket >> targets.ReadForCaster(pet); targets.ReadAdditionalData(recvPacket, cast_flags); pet->clearUnitState(UNIT_STAT_MOVING); Spell* spell = new Spell(pet, spellInfo, triggeredByAura ? true : false, pet->GetObjectGuid(), triggeredByAura ? triggeredByAura->GetSpellProto() : NULL); spell->m_cast_count = cast_count; // probably pending spell cast spell->m_targets = targets; SpellCastResult result = triggeredByAura ? SPELL_CAST_OK : spell->CheckPetCast(NULL); if (result == SPELL_CAST_OK) { pet->AddCreatureSpellCooldown(spellid); if (pet->IsPet()) { // 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*)pet)->getPetType() == SUMMON_PET && (urand(0, 100) < 10)) pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL); else pet->SendPetAIReaction(); } spell->prepare(&(spell->m_targets), triggeredByAura); } else { Unit* owner = pet->GetCharmerOrOwner(); if (owner && owner->GetTypeId() == TYPEID_PLAYER && !triggeredByAura) Spell::SendCastResult((Player*)owner, spellInfo, 0, result, true); if (!pet->HasSpellCooldown(spellid) && !triggeredByAura) GetPlayer()->SendClearCooldown(spellid, pet); spell->finish(false); delete spell; } }
void WorldSession::HandlePetAction(WorldPacket & recv_data) { uint64 guid1; uint16 spellid; uint16 flag; uint64 guid2; recv_data >> guid1; //pet guid recv_data >> spellid; recv_data >> flag; //delete = 0x0700 CastSpell = C100 recv_data >> guid2; //tag guid // 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)), flag, spellid, uint32(GUID_LOPART(guid2))); if (!pet) { sLog.outError("Pet %u not exist.", uint32(GUID_LOPART(guid1))); return; } if (pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharm()) { sLog.outError("HandlePetAction.Pet %u isn't pet of player %s.", uint32(GUID_LOPART(guid1)), GetPlayer()->GetName()); return; } if (!pet->isAlive()) return; if (pet->GetTypeId() == TYPEID_PLAYER && !(flag == ACT_COMMAND && spellid == COMMAND_ATTACK)) return; 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: //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 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->ToCreature()->isPet()) { 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); } else // charmed or possessed _player->Uncharm(); break; default: sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", flag, spellid); } break; case ACT_REACTION: // 0x600 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: //0x8100 spell (disabled), ignore case ACT_CAST: //0x0100 case ACT_ENABLED: //0xc100 spell { Unit* unit_target = NULL; if (pet->ToCreature()->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", spellid); 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); int16 result = spell->PetCanCast(unit_target); //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(unit_target->ToPlayer()); if (Unit* powner = pet->GetCharmerOrOwner()) if (powner->GetTypeId() == TYPEID_PLAYER) pet->SendUpdateToPlayer(powner->ToPlayer()); result = -1; } if (result == -1) { pet->ToCreature()->AddCreatureSpellCooldown(spellid); if (pet->ToCreature()->isPet()) ((Pet*)pet)->CheckLearning(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()) { // 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()) { 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); } else pet->SendPetCastFail(spellid, result); if (!pet->ToCreature()->HasSpellCooldown(spellid)) pet->SendPetClearCooldown(spellid); 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.", flag, spellid); } }
void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) { sLog.outDetail("WORLD: CMSG_PET_CAST_SPELL"); CHECK_PACKET_SIZE(recvPacket,8+4); uint64 guid; uint32 spellid; recvPacket >> guid >> spellid; // This opcode is also sent from charmed and possessed units (players and creatures) if(!_player->GetPet() && !_player->GetCharm()) return; Unit* caster = ObjectAccessor::GetUnit(*_player, guid); if(!caster || (caster != _player->GetPet() && caster != _player->GetCharm())) { sLog.outError( "HandlePetCastSpellOpcode: Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); return; } SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid); if(!spellInfo) { sLog.outError("WORLD: unknown PET spell id %i\n", spellid); return; } // do not cast not learned spells if(!caster->HasSpell(spellid) || IsPassiveSpell(spellid)) return; if (spellInfo->StartRecoveryCategory > 0) //Check if spell is affected by GCD if (caster->GetTypeId() == TYPEID_UNIT && ((Creature*)caster)->GetGlobalCooldown() > 0) { caster->SendPetCastFail(spellid, SPELL_FAILED_NOT_READY); return; } SpellCastTargets targets; if(!targets.read(&recvPacket,caster)) return; caster->clearUnitState(UNIT_STAT_FOLLOW); caster->InterruptNonMeleeSpells(false); Spell *spell = new Spell(caster, spellInfo, false); spell->m_targets = targets; int16 result = spell->PetCanCast(NULL); if(result == -1) { if(caster->GetTypeId() == TYPEID_UNIT) { Creature* pet = (Creature*)caster; pet->AddCreatureSpellCooldown(spellid); 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)) 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(!((Player*)caster)->HasSpellCooldown(spellid)) caster->SendPetClearCooldown(spellid); } else { if(!((Creature*)caster)->HasSpellCooldown(spellid)) caster->SendPetClearCooldown(spellid); } spell->finish(false); delete spell; } }
bool TargetedMovementGeneratorMedium<T,D>::DoUpdate(T* owner, uint32 time_diff) { if (!i_target.isValid() || !i_target->IsInWorld()) return false; if (!owner || !owner->isAlive()) return false; if (owner->HasUnitState(UNIT_STATE_NOT_MOVE)) { D::_clearUnitStateMove(owner); return true; } // prevent movement while casting spells with cast time or channel time if (owner->HasUnitState(UNIT_STATE_CASTING)) { if (!owner->IsStopped()) owner->StopMoving(); return true; } // prevent crash after creature killed pet if (static_cast<D*>(this)->_lostTarget(owner)) { D::_clearUnitStateMove(owner); return true; } // Check whether pet got send to cast a spell if (i_spell) { SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(i_spell); Spell* spell = new Spell(owner->ToUnit(), spellInfo, TRIGGERED_NONE); SpellCastResult result = spell->CheckPetCast(i_target.getTarget()); if (result == SPELL_CAST_OK) { spell->prepare(&(spell->m_targets)); // Reset i_spell here to prevent chaincasting. i_spell = 0; } else { spell->finish(false); delete spell; } } bool targetMoved = false; i_recheckDistance.Update(time_diff); if (i_recheckDistance.Passed()) { i_recheckDistance.Reset(100); //More distance let have better performance, less distance let have more sensitive reaction at target move. float allowed_dist = owner->GetCombatReach() + sWorld->getRate(RATE_TARGET_POS_RECALCULATION_RANGE); G3D::Vector3 dest = owner->movespline->FinalDestination(); targetMoved = !(i_target->IsWithinDist3d(dest.x, dest.y, dest.z, allowed_dist) && i_target->IsWithinLOS(dest.x, dest.y, dest.z)); } if (i_recalculateTravel || targetMoved) _setTargetLocation(owner, targetMoved); if (owner->movespline->Finalized()) { static_cast<D*>(this)->MovementInform(owner); if (i_angle == 0.f && !owner->HasInArc(0.01f, i_target.getTarget())) owner->SetInFront(i_target.getTarget()); if (!i_targetReached) { i_targetReached = true; static_cast<D*>(this)->_reachTarget(owner); } } return true; }
void WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket) { TC_LOG_DEBUG("network", "WORLD: CMSG_PET_CAST_SPELL"); ObjectGuid casterGUID; ObjectGuid unkGUID1; ObjectGuid transportDstGUID; ObjectGuid transportSrcGUID; ObjectGuid targetGUID; ObjectGuid unkGUID2; bool hasDestPos; bool hasSrcPos; bool hasSpeed; bool hasSpell; bool hasGlyphIndex; bool hasTargetFlags; bool hasElevation; bool hasString; bool hasCastCount; bool hasUnk5bits; uint32 archeologyCounter = 0; WorldLocation dstLoc, srcLoc; float speed = 0.0f; float elevation = 0.0f; uint32 targetFlags = 0; uint32 spellID = 0; uint32 stringLenght = 0; uint8 castCount = 0; recvPacket.ReadBitSeq<3, 5>(casterGUID); hasDestPos = recvPacket.ReadBit(); recvPacket.ReadBit(); // unk bit hasSpeed = !recvPacket.ReadBit(); hasSrcPos = recvPacket.ReadBit(); hasSpell = !recvPacket.ReadBit(); recvPacket.ReadBitSeq<0>(casterGUID); hasGlyphIndex = !recvPacket.ReadBit(); recvPacket.ReadBitSeq<7>(casterGUID); hasTargetFlags = !recvPacket.ReadBit(); hasElevation = !recvPacket.ReadBit(); recvPacket.ReadBit(); // has movement info hasString = !recvPacket.ReadBit(); recvPacket.ReadBit(); // !inverse bit, unk hasCastCount = !recvPacket.ReadBit(); recvPacket.ReadBitSeq<2, 4>(casterGUID); archeologyCounter = recvPacket.ReadBits(2); recvPacket.ReadBitSeq<1>(casterGUID); hasUnk5bits = !recvPacket.ReadBit(); for (uint32 i = 0; i < archeologyCounter; i++) recvPacket.ReadBits(2); // archeology type recvPacket.ReadBitSeq<6>(casterGUID); if (hasDestPos) recvPacket.ReadBitSeq<2, 7, 4, 0, 1, 6, 5, 3>(transportDstGUID); // movement block (disabled by patch client-side) if (hasSrcPos) recvPacket.ReadBitSeq<6, 2, 3, 1, 5, 4, 0, 7>(transportSrcGUID); if (hasUnk5bits) recvPacket.ReadBits(5); // unk 5 bits // Target GUID recvPacket.ReadBitSeq<3, 5, 6, 2, 4, 1, 7, 0>(targetGUID); // unkGUID1 recvPacket.ReadBitSeq<3, 1, 5, 2, 4, 7, 0, 6>(unkGUID1); if (hasTargetFlags) targetFlags = recvPacket.ReadBits(20); if (hasString) stringLenght = recvPacket.ReadBits(7); recvPacket.ReadByteSeq<0, 4, 5, 1, 2, 3, 7>(casterGUID); for (uint32 i = 0; i < archeologyCounter; i++) { recvPacket.read_skip<uint32>(); // entry recvPacket.read_skip<uint32>(); // counter } recvPacket.ReadByteSeq<6>(casterGUID); recvPacket.ReadByteSeq<1, 5, 4, 2, 7, 3, 0>(unkGUID1); if (hasSrcPos) { recvPacket.ReadByteSeq<4>(transportSrcGUID); srcLoc.m_positionY = recvPacket.read<float>(); recvPacket.ReadByteSeq<2, 6>(transportSrcGUID); srcLoc.m_positionZ = recvPacket.read<float>(); srcLoc.m_positionX = recvPacket.read<float>(); recvPacket.ReadByteSeq<1, 3, 5, 7, 0>(transportSrcGUID); } // Target GUID recvPacket.ReadByteSeq<7, 4, 2, 6, 3, 0, 5, 1>(targetGUID); if (hasDestPos) { dstLoc.m_positionX = recvPacket.read<float>(); recvPacket.ReadByteSeq<5, 7, 2, 0, 1, 3, 6>(transportDstGUID); dstLoc.m_positionZ = recvPacket.read<float>(); dstLoc.m_positionY = recvPacket.read<float>(); recvPacket.ReadByteSeq<4>(transportDstGUID); } if (hasGlyphIndex) recvPacket.read_skip<uint32>(); // glyph index if (hasElevation) elevation = recvPacket.read<float>(); if (hasSpell) spellID = recvPacket.read<uint32>(); if (hasCastCount) castCount = recvPacket.read<uint8>(); if (hasString) recvPacket.ReadString(stringLenght); if (hasSpeed) speed = recvPacket.read<float>(); TC_LOG_DEBUG("network", "WORLD: CMSG_PET_CAST_SPELL, castCount: %u, spellId %u, targetFlags %u", castCount, spellID, targetFlags); // This opcode is also sent from charmed and possessed units (players and creatures) if (!_player->GetGuardianPet() && !_player->GetCharm()) return; Unit* caster = ObjectAccessor::GetUnit(*_player, casterGUID); if (!caster || (caster != _player->GetGuardianPet() && caster != _player->GetCharm())) { TC_LOG_ERROR("network", "HandlePetCastSpellOpcode: Pet %u isn't pet of player %s .", uint32(GUID_LOPART(casterGUID)), GetPlayer()->GetName().c_str()); return; } SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID); if (!spellInfo) { TC_LOG_ERROR("network", "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(caster, 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.Initialize(targetFlags, targetGUID, unkGUID1, transportDstGUID, dstLoc, transportSrcGUID, srcLoc); targets.SetElevation(elevation); targets.SetSpeed(speed); targets.Update(caster); // Interrupt auto-cast if other spell is forced by player if (caster->IsNonMeleeSpellCasted(false, true, true, false, true)) caster->InterruptNonMeleeSpells(false, 0, false); caster->ClearUnitState(UNIT_STATE_FOLLOW); Spell* spell = new Spell(caster, spellInfo, TRIGGERED_NONE, 0, false, true); 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(spellID); } } 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::HandlePetCastSpellOpcode(WorldPacket& recvPacket) { TC_LOG_DEBUG("network", "WORLD: CMSG_PET_CAST_SPELL"); ObjectGuid guid; uint8 castCount; uint32 spellId; uint8 castFlags; recvPacket >> guid >> castCount >> spellId >> castFlags; TC_LOG_DEBUG("network", "WORLD: CMSG_PET_CAST_SPELL, %s, castCount: %u, spellId %u, castFlags %u", guid.ToString().c_str(), 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())) { TC_LOG_ERROR("network", "HandlePetCastSpellOpcode: %s isn't pet of player %s (%s).", guid.ToString().c_str(), GetPlayer()->GetName().c_str(), GetPlayer()->GetGUID().ToString().c_str()); return; } SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo) { TC_LOG_ERROR("network", "WORLD: unknown PET spell id %i", spellId); 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_STATE_FOLLOW); Spell* spell = new Spell(caster, spellInfo, TRIGGERED_NONE); spell->m_cast_count = castCount; // probably pending spell cast spell->m_targets = targets; SpellCastResult result = spell->CheckPetCast(NULL); if (result == SPELL_CAST_OK) { if (Creature* creature = caster->ToCreature()) { creature->AddCreatureSpellCooldown(spellId); if (Pet* pet = creature->ToPet()) { // 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->getPetType() == SUMMON_PET && (urand(0, 100) < 10)) pet->SendPetTalk(PET_TALK_SPECIAL_SPELL); else pet->SendPetAIReaction(guid); } } spell->prepare(&(spell->m_targets)); } else { spell->SendPetCastResult(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::HandlePetCastSpellOpcode(WorldPacket& recvPacket) { DETAIL_LOG("WORLD: CMSG_PET_CAST_SPELL"); ObjectGuid guid; uint32 spellid; recvPacket >> guid >> spellid; DEBUG_LOG("WORLD: CMSG_PET_CAST_SPELL, %s, spellid %u", guid.GetString().c_str(), spellid); Creature* pet = _player->GetMap()->GetAnyTypeCreature(guid); if (!pet || (guid != _player->GetPetGuid() && guid != _player->GetCharmGuid())) { sLog.outError("HandlePetCastSpellOpcode: %s isn't pet of %s .", guid.GetString().c_str(), GetPlayer()->GetGuidStr().c_str()); return; } SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellid); if (!spellInfo) { sLog.outError("WORLD: unknown PET spell id %i", spellid); return; } if (pet->GetCharmInfo() && pet->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) return; // do not cast not learned spells if (!pet->HasSpell(spellid) || IsPassiveSpell(spellInfo)) return; SpellCastTargets targets; recvPacket >> targets.ReadForCaster(pet); pet->clearUnitState(UNIT_STAT_MOVING); Spell* spell = new Spell(pet, spellInfo, false); spell->m_targets = targets; SpellCastResult result = spell->CheckPetCast(nullptr); if (result == SPELL_CAST_OK) { pet->AddCreatureSpellCooldown(spellid); if (pet->IsPet()) ((Pet*)pet)->CheckLearning(spellid); spell->SpellStart(&(spell->m_targets)); } else { Unit* owner = pet->GetCharmerOrOwner(); if (owner && owner->GetTypeId() == TYPEID_PLAYER) Spell::SendCastResult((Player*)owner, spellInfo, 0, result, true); if (!pet->HasSpellCooldown(spellid)) GetPlayer()->SendClearCooldown(spellid, pet); spell->finish(false); delete spell; } }
void WorldSession::HandlePetAction( WorldPacket & recv_data ) { CHECK_PACKET_SIZE(recv_data, 8+2+2+8); uint64 guid1; uint16 spellid; uint16 flag; uint64 guid2; recv_data >> guid1; //pet guid recv_data >> spellid; recv_data >> flag; //delete = 0x0700 CastSpell = C100 recv_data >> guid2; //tag guid // 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)), flag, spellid, uint32(GUID_LOPART(guid2)) ); if(!pet) { sLog.outError( "Pet %u not exist.", uint32(GUID_LOPART(guid1)) ); return; } if(pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharm()) { sLog.outError("HandlePetAction.Pet %u isn't pet of player %s.", uint32(GUID_LOPART(guid1)), GetPlayer()->GetName() ); return; } if(!pet->isAlive()) return; if(pet->GetTypeId() == TYPEID_PLAYER && !(flag == ACT_COMMAND && spellid == COMMAND_ATTACK)) return; CharmInfo *charmInfo = pet->GetCharmInfo(); if(!charmInfo) { sLog.outError("WorldSession::HandlePetAction: object "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID()); return; } switch(flag) { case ACT_COMMAND: //0x0700 switch(spellid) { case COMMAND_STAY: //flat=1792 //STAY pet->StopMoving(); pet->GetMotionMaster()->Clear(); pet->GetMotionMaster()->MoveIdle(); charmInfo->SetCommandState( COMMAND_STAY ); break; case COMMAND_FOLLOW: //spellid=1792 //FOLLOW pet->AttackStop(); pet->GetMotionMaster()->MoveFollow(_player,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); charmInfo->SetCommandState( COMMAND_FOLLOW ); break; case COMMAND_ATTACK: //spellid=1792 //ATTACK { const uint64& selguid = _player->GetSelection(); Unit *TargetUnit = ObjectAccessor::GetUnit(*_player, selguid); if(!TargetUnit) return; // not let attack friendly units. if(GetPlayer()->IsFriendlyTo(TargetUnit)) return; // Not let attack through obstructions if(!pet->IsWithinLOSInMap(TargetUnit)) return; // This is true if pet has no target or has target but targets differs. if(pet->getVictim() != TargetUnit) { if (pet->getVictim()) pet->AttackStop(); if(pet->GetTypeId() != TYPEID_PLAYER) { pet->GetMotionMaster()->Clear(); if (((Creature*)pet)->AI()) ((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 { pet->Attack(TargetUnit,true); pet->SendPetAIReaction(guid1); } } break; } case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet) if(((Creature*)pet)->isPet()) { 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); } else // charmed _player->Uncharm(); break; default: sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", flag, spellid); } break; case ACT_REACTION: // 0x600 switch(spellid) { case REACT_PASSIVE: //passive case REACT_DEFENSIVE: //recovery case REACT_AGGRESSIVE: //activete charmInfo->SetReactState( ReactStates(spellid) ); break; } break; case ACT_DISABLED: // 0x8100 spell (disabled), ignore case ACT_PASSIVE: // 0x0100 case ACT_ENABLED: // 0xC100 spell { 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", spellid); return; } for(uint32 i = 0; i < 3; ++i) { if(spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA || spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA_INSTANT || spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED) return; } // do not cast not learned spells if(!pet->HasSpell(spellid) || IsPassiveSpell(spellid)) return; pet->clearUnitState(UNIT_STAT_FOLLOW); 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->HasAuraType(SPELL_AURA_MOD_POSSESS)) { 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 = SPELL_CAST_OK; } if(result == SPELL_CAST_OK) { ((Creature*)pet)->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(((Creature*)pet)->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->HasAuraType(SPELL_AURA_MOD_POSSESS)) { // 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)->AI()) ((Creature*)pet)->AI()->AttackStart(unit_target); } } spell->prepare(&(spell->m_targets)); } else { if(pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) { WorldPacket data(SMSG_CAST_FAILED, (4+1+1)); data << uint8(0) << uint32(spellid) << uint8(result); switch (result) { case SPELL_FAILED_REQUIRES_SPELL_FOCUS: data << uint32(spellInfo->RequiresSpellFocus); break; default: break; } SendPacket(&data); } else pet->SendPetCastFail(spellid, result); if(!((Creature*)pet)->HasSpellCooldown(spellid)) pet->SendPetClearCooldown(spellid); spell->finish(false); delete spell; } break; } default: sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", flag, spellid); } }
void WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket) { ;//sLog->outDebug(LOG_FILTER_NETWORKIO, "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().c_str()); return; } SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo) { sLog->outError("WORLD: unknown PET spell id %i", spellId); return; } // do not cast not learned spells if (!caster->HasSpell(spellId) || spellInfo->IsPassive()) return; SpellCastTargets targets; targets.Read(recvPacket, caster); HandleClientCastFlags(recvPacket, castFlags, targets); bool SetFollow = caster->HasUnitState(UNIT_STATE_FOLLOW); caster->ClearUnitState(UNIT_STATE_FOLLOW); Spell* spell = new Spell(caster, spellInfo, TRIGGERED_NONE); spell->m_cast_count = castCount; // probably pending spell cast spell->m_targets = targets; spell->LoadScripts(); // Xinef: Send default target, fixes return on NeedExplicitUnitTarget Unit* target = targets.GetUnitTarget(); if (!target && spell->m_spellInfo->NeedsExplicitUnitTarget()) target = _player->GetSelectedUnit(); SpellCastResult result = spell->CheckPetCast(target); if (result == SPELL_CAST_OK) { if (Creature* creature = caster->ToCreature()) { creature->AddSpellCooldown(spellId, 0, 0); if (Pet* pet = creature->ToPet()) { // 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->getPetType() == SUMMON_PET && (urand(0, 100) < 10)) pet->SendPetTalk(PET_TALK_SPECIAL_SPELL); else pet->SendPetAIReaction(guid); } } spell->prepare(&(spell->m_targets)); } else { if (!caster->GetCharmInfo() || !caster->GetCharmInfo()->GetForcedSpell()) spell->SendPetCastResult(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); // reset specific flags in case of spell fail. AI will reset other flags if (caster->IsPet()) caster->PetSpellFail(spellInfo, targets.GetUnitTarget(), result); } spell->finish(false); delete spell; } if (SetFollow && !caster->IsInCombat()) caster->AddUnitState(UNIT_STATE_FOLLOW); }
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::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 { bool controlledMotion = pet->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) != NULL_MOTION_TYPE; if (!controlledMotion) { pet->StopMovingOnCurrentPos(); pet->GetMotionMaster()->Clear(false); pet->GetMotionMaster()->MoveIdle(); } charmInfo->SetCommandState(COMMAND_STAY); charmInfo->SetIsCommandAttack(false); charmInfo->SetIsCommandFollow(false); charmInfo->SetIsFollowing(false); charmInfo->SetIsReturning(false); charmInfo->SetIsAtStay(!controlledMotion); charmInfo->SaveStayPosition(controlledMotion); charmInfo->SetForcedSpell(0); charmInfo->SetForcedTargetGUID(0); break; } case COMMAND_FOLLOW: //spellid=1792 //FOLLOW { pet->AttackStop(); pet->InterruptNonMeleeSpells(false); pet->ClearInPetCombat(); pet->GetMotionMaster()->MoveFollow(_player, PET_FOLLOW_DIST, pet->GetFollowAngle()); charmInfo->SetCommandState(COMMAND_FOLLOW); charmInfo->SetIsCommandAttack(false); charmInfo->SetIsAtStay(false); charmInfo->SetIsReturning(true); charmInfo->SetIsCommandFollow(true); charmInfo->SetIsFollowing(false); charmInfo->SetForcedSpell(0); charmInfo->SetForcedTargetGUID(0); 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; // pussywizard: if (Creature* creaturePet = pet->ToCreature()) if (!creaturePet->_CanDetectFeignDeathOf(TargetUnit) || !creaturePet->CanCreatureAttack(TargetUnit) || creaturePet->isTargetNotAcceptableByMMaps(TargetUnit->GetGUID(), sWorld->GetGameTime(), TargetUnit)) return; // Not let attack through obstructions bool checkLos = !MMAP::MMapFactory::IsPathfindingEnabled(pet->GetMap()) || (TargetUnit->GetTypeId() == TYPEID_UNIT && (TargetUnit->ToCreature()->isWorldBoss() || TargetUnit->ToCreature()->IsDungeonBoss())); if (checkLos && !pet->IsWithinLOSInMap(TargetUnit)) { WorldPacket data(SMSG_CAST_FAILED, 1+4+1); data << uint8(0); data << uint32(7389); data << uint8(SPELL_FAILED_LINE_OF_SIGHT); SendPacket(&data); return; } pet->ClearUnitState(UNIT_STATE_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())) { pet->AttackStop(); if (pet->GetTypeId() != TYPEID_PLAYER && pet->ToCreature()->IsAIEnabled) { charmInfo->SetIsCommandAttack(true); charmInfo->SetIsAtStay(false); charmInfo->SetIsFollowing(false); charmInfo->SetIsCommandFollow(false); charmInfo->SetIsReturning(false); pet->ToCreature()->AI()->AttackStart(TargetUnit); //10% chance to play special pet attack talk, else growl if (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 { charmInfo->SetIsCommandAttack(true); charmInfo->SetIsAtStay(false); charmInfo->SetIsFollowing(false); charmInfo->SetIsCommandFollow(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()) { if (pet->IsSummon()) pet->ToTempSummon()->UnSummon(); else _player->StopCastingCharm(); } else if (pet->GetOwnerGUID() == GetPlayer()->GetGUID()) { ASSERT(pet->GetTypeId() == TYPEID_UNIT); if (pet->IsPet()) { if (pet->ToPet()->getPetType() == HUNTER_PET) GetPlayer()->RemovePet(pet->ToPet(), 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|UNIT_MASK_SUMMON|UNIT_MASK_GUARDIAN|UNIT_MASK_CONTROLABLE_GUARDIAN)) { pet->ToTempSummon()->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(); pet->ClearInPetCombat(); case REACT_DEFENSIVE: //recovery case REACT_AGGRESSIVE: //activete if (pet->GetTypeId() == TYPEID_UNIT) pet->ToCreature()->SetReactState(ReactStates(spellid)); else charmInfo->SetPlayerReactState(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; } 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 charmInfo->SetIsAtStay(false); charmInfo->SetIsCommandAttack(!pet->ToCreature()->HasReactState(REACT_PASSIVE)); charmInfo->SetIsReturning(false); charmInfo->SetIsFollowing(false); Spell* spell = new Spell(pet, spellInfo, TRIGGERED_NONE); spell->LoadScripts(); // xinef: load for CheckPetCast 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(unit_target->ToPlayer()); } else if (Unit* unit_target2 = spell->m_targets.GetUnitTarget()) { pet->SetInFront(unit_target2); if (unit_target2->GetTypeId() == TYPEID_PLAYER) pet->SendUpdateToPlayer(unit_target2->ToPlayer()); } 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()->AddSpellCooldown(spellid, 0, 0); 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->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->ToCreature()->IsAIEnabled) pet->ToCreature()->AI()->AttackStart(unit_target); } } spell->prepare(&(spell->m_targets)); charmInfo->SetForcedSpell(0); charmInfo->SetForcedTargetGUID(0); } else { // dont spam alerts if (!charmInfo->GetForcedSpell()) { if (pet->isPossessed() || pet->IsVehicle()) Spell::SendCastResult(GetPlayer(), spellInfo, 0, result); else spell->SendPetCastResult(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 pet->PetSpellFail(spellInfo, unit_target, result); } break; } default: sLog->outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid); } }
void WorldSession::HandlePetAction(WorldPacket& recv_data) { ObjectGuid petGuid; uint32 data; ObjectGuid targetGuid; float x, y, z; recv_data >> petGuid; recv_data >> data; recv_data >> targetGuid; recv_data >> x >> y >> z; uint32 spellid = UNIT_ACTION_BUTTON_ACTION(data); uint8 flag = UNIT_ACTION_BUTTON_TYPE(data); // delete = 0x07 CastSpell = C1 DETAIL_LOG("HandlePetAction: %s flag is %u, spellid is %u, target %s.", petGuid.GetString().c_str(), uint32(flag), spellid, targetGuid.GetString().c_str()); // used also for charmed creature/player Unit* pet = _player->GetMap()->GetUnit(petGuid); if (!pet) { sLog.outError("HandlePetAction: %s not exist.", petGuid.GetString().c_str()); return; } if (_player->GetObjectGuid() != pet->GetCharmerOrOwnerGuid()) { sLog.outError("HandlePetAction: %s isn't controlled by %s.", petGuid.GetString().c_str(), _player->GetGuidStr().c_str()); return; } if (!pet->isAlive()) return; if (pet->GetTypeId() == TYPEID_PLAYER && pet->GetCharmer()->GetTypeId() == TYPEID_PLAYER) { // controller player cannot use controlled player's spells if (flag != (ACT_COMMAND || ACT_REACTION)) return; } else if (((Creature*)pet)->IsPet()) { // pet can have action bar disabled if (((Pet*)pet)->GetModeFlags() & PET_MODE_DISABLE_ACTIONS) return; } 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->StopMoving(); pet->AttackStop(true, true); pet->GetMotionMaster()->Clear(); ((Pet*)pet)->SetStayPosition(true); ((Pet*)pet)->SetIsRetreating(); ((Pet*)pet)->SetSpellOpener(); charmInfo->SetCommandState(COMMAND_STAY); break; } case COMMAND_FOLLOW: // spellid=1792 // FOLLOW { pet->StopMoving(); pet->AttackStop(true, true); pet->GetMotionMaster()->Clear(); ((Pet*)pet)->SetStayPosition(); ((Pet*)pet)->SetIsRetreating(true); ((Pet*)pet)->SetSpellOpener(); charmInfo->SetCommandState(COMMAND_FOLLOW); break; } case COMMAND_ATTACK: // spellid=1792 // ATTACK { ((Pet*)pet)->SetIsRetreating(); ((Pet*)pet)->SetSpellOpener(); Unit* targetUnit = targetGuid ? _player->GetMap()->GetUnit(targetGuid) : nullptr; if (targetUnit && targetUnit != pet && targetUnit->isTargetableForAttack() && targetUnit->isInAccessablePlaceFor((Creature*)pet)) { _player->SetInCombatState(true, targetUnit); // This is true if pet has no target or has target but targets differs. if (pet->getVictim() != targetUnit) { pet->AttackStop(); pet->GetMotionMaster()->Clear(); if (((Creature*)pet)->AI()) { ((Creature*)pet)->AI()->AttackStart(targetUnit); // 10% chance to play special warlock pet attack talk, else growl if (((Creature*)pet)->IsPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && roll_chance_i(10)) pet->SendPetTalk((uint32)PET_TALK_ATTACK); pet->SendPetAIReaction(); } else pet->Attack(targetUnit, true); } } break; } case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet) { Creature* petC = (Creature*)pet; if (petC->IsPet()) { Pet* p = (Pet*)petC; if (p->getPetType() == HUNTER_PET) p->Unsummon(PET_SAVE_AS_DELETED, _player); else // dismissing a summoned pet is like killing them (this prevents returning a soulshard...) p->SetDeathState(CORPSE); } else // charmed _player->Uncharm(); if (petC->IsTemporarySummon()) // special case when pet was temporary summon through DoSummonPossesed { petC->ForcedDespawn(); return; } ((Pet*)pet)->SetStayPosition(); 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(true, true); ((Pet*)pet)->SetSpellOpener(); } case REACT_DEFENSIVE: // recovery case REACT_AGGRESSIVE: // activete { charmInfo->SetReactState(ReactStates(spellid)); break; } } break; case ACT_DISABLED: // 0x81 spell (disabled), ignore case ACT_PASSIVE: // 0x01 case ACT_ENABLED: // 0xC1 spell { ((Pet*)pet)->SetIsRetreating(); ((Pet*)pet)->SetSpellOpener(); Unit* unit_target = targetGuid ? _player->GetMap()->GetUnit(targetGuid) : nullptr; // do not cast unknown spells SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellid); if (!spellInfo) { sLog.outError("WORLD: unknown PET spell id %i", spellid); return; } if (pet->GetCharmInfo() && pet->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) return; for (int i = 0; i < MAX_EFFECT_INDEX; ++i) { SpellEffectEntry const* spellEffect = spellInfo->GetSpellEffect(SpellEffectIndex(i)); if (!spellEffect) continue; if (spellEffect->EffectImplicitTargetA == TARGET_ALL_ENEMY_IN_AREA || spellEffect->EffectImplicitTargetA == TARGET_ALL_ENEMY_IN_AREA_INSTANT || spellEffect->EffectImplicitTargetA == TARGET_ALL_ENEMY_IN_AREA_CHANNELED) return; } // do not cast not learned spells if (!pet->HasSpell(spellid) || IsPassiveSpell(spellInfo)) return; pet->clearUnitState(UNIT_STAT_MOVING); Spell* spell = new Spell(pet, spellInfo, false); SpellCastResult result = spell->CheckPetCast(unit_target); const SpellRangeEntry* sRange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex); if (unit_target && !(pet->IsWithinDistInMap(unit_target, sRange->maxRange) && pet->IsWithinLOSInMap(unit_target)) && !(GetPlayer()->IsFriendlyTo(unit_target) || pet->HasAuraType(SPELL_AURA_MOD_POSSESS))) { ((Pet*)pet)->SetSpellOpener(spellid, sRange->minRange, sRange->maxRange); spell->finish(false); delete spell; pet->AttackStop(); pet->GetMotionMaster()->Clear(); ((Creature*)pet)->AI()->AttackStart(unit_target); // 10% chance to play special warlock pet attack talk, else growl if (((Creature*)pet)->IsPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != unit_target && roll_chance_i(10)) pet->SendPetTalk((uint32)PET_TALK_ATTACK); pet->SendPetAIReaction(); return; } // auto turn to target unless possessed if (result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) { if (unit_target) { pet->SetInFront(unit_target); if (unit_target->GetTypeId() == TYPEID_PLAYER) pet->SendCreateUpdateToPlayer((Player*)unit_target); } else if (Unit* unit_target2 = spell->m_targets.getUnitTarget()) { pet->SetInFront(unit_target2); if (unit_target2->GetTypeId() == TYPEID_PLAYER) pet->SendCreateUpdateToPlayer((Player*)unit_target2); } if (Unit* powner = pet->GetCharmerOrOwner()) if (powner->GetTypeId() == TYPEID_PLAYER) pet->SendCreateUpdateToPlayer((Player*)powner); result = SPELL_CAST_OK; } if (result == SPELL_CAST_OK) { ((Creature*)pet)->AddCreatureSpellCooldown(spellid); unit_target = spell->m_targets.getUnitTarget(); if (unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) { // This is true if pet has no target or has target but targets differs. if (pet->getVictim() != unit_target) { pet->AttackStop(); pet->GetMotionMaster()->Clear(); _player->SetInCombatState(true, unit_target); if (((Creature*)pet)->AI()) { ((Creature*)pet)->AI()->AttackStart(unit_target); // 10% chance to play special warlock pet attack talk, else growl if (((Creature*)pet)->IsPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != unit_target && roll_chance_i(10)) pet->SendPetTalk((uint32)PET_TALK_ATTACK); pet->SendPetAIReaction(); } else pet->Attack(unit_target, true); } } ((Pet*)pet)->SetSpellOpener(); spell->SpellStart(&(spell->m_targets)); } else { if (pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) Spell::SendCastResult(GetPlayer(), spellInfo, 0, result); else { Unit* owner = pet->GetCharmerOrOwner(); if (owner && owner->GetTypeId() == TYPEID_PLAYER) Spell::SendCastResult((Player*)owner, spellInfo, 0, result, true); } if (!((Creature*)pet)->HasSpellCooldown(spellid)) GetPlayer()->SendClearCooldown(spellid, pet); ((Pet*)pet)->SetSpellOpener(); spell->finish(false); delete spell; } break; } default: sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid); } }
void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) { DETAIL_LOG("WORLD: CMSG_PET_CAST_SPELL"); recvPacket.hexlike(); recvPacket.print_storage(); //2 - 0 - 0 - 43 - 129 - 0 - 80 - 241 | - 42 - 211 - 253 - 0 | - 0 | - 2 |- 96 - 0 - 0 - 0 | - 0 - 26 //- 164 - 59 - 196 - 174 - 98 - 131 | - 194 - 182 - 171 - 218| - 67 - 0 - 48 - 93| - 0 - 196 - 32 //- 177| - 242 - 193 - 22 - 110 - 224 - 67 - 203 - 166 | - 68 - 61 - 133 - 1| - 240 - 66 - 1 - 183 | //- 0 - 0 - 0 - 217| - 2 - 43 - 129 - 80 - 241 - 0 - 10 - 0 - 0 - 0 - 0 - 76 - 109 - 175 - 0 //- 238 - 115 - 58 - 196 - 20 - 110 - 121 - 194 - 187 - 107 - 217 - 67 - 32 - 44 - 27 - 62 - 217 //- 1 - 36 - 129 - 80 - 241 - 0 - 0 - 160 - 64 - 0 - 0 - 160 - 64 - 0 - 0 - 160 - 64 - 192 - 233 //- 172 - 62 - 4 - 0 - 0 - 0 - 7 - 230 - 0 - 0 - 0 - //5 - 0 - 0 - 43 - 129 - 0 - 80 - 241 | - 85 - 211 - 253 - 0 | - 0 | - 2 | - 96 - 0 - 0 - 0 | - 0 - 69 - 60 - 61 //- 196 - 171 - 248 - 107| - 194 - 8 - 236 - 218 | - 67 - 0 - 177 - 11 | - 46 - 196 - 89 - 16 | - 14 - 195 //- 5 - 38 - 231 - 67 - 23 - 221 | - 110 - 62 - 15 - 3 | - 240 - 66 -| 1 - 183 | - 0 - 0 - 0 - 217 | - 5 - 43 //- 129 - 80 - 241 - 0 - 10 - 0 - 0 - 0 - 0 - 233 - 41 - 203 - 0 - 106 - 207 - 59 - 196 - 179 - 173 - 83 //- 194 - 8 - 108 - 217 - 67 - 127 - 153 - 170 - 64 - 217 - 4 - 36 - 129 - 80 - 241 - 0 - 0 - 160 - 64 //- 0 - 0 - 160 - 64 - 0 - 0 - 160 - 64 - 7 - 77 - 175 - 64 - 4 - 0 - 0 - 0 - 7 - 195 - 0 - 0 - 0 - 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; DEBUG_LOG("WORLD: CMSG_PET_CAST_SPELL, cast_count: %u, spellid %u, unk_flags %u", cast_count, spellid, unk_flags); if (!_player->GetPet() && !_player->GetCharm()) return; if (GUID_HIPART(guid) == HIGHGUID_PLAYER) return; Creature* pet = ObjectAccessor::GetAnyTypeCreature(*_player,guid); if (!pet || (pet != _player->GetPet() && pet!= _player->GetCharm())) { sLog.outError( "HandlePetCastSpellOpcode: Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); return; } if (pet->GetGlobalCooldown() > 0) return; SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid); if (!spellInfo) { sLog.outError("WORLD: unknown PET spell id %i", spellid); return; } // do not cast not learned spells if (!pet->HasSpell(spellid) || IsPassiveSpell(spellInfo)) return; SpellCastTargets targets; recvPacket >> targets.ReadForCaster(pet); pet->clearUnitState(UNIT_STAT_MOVING); Spell *spell = new Spell(pet, spellInfo, false); spell->m_cast_count = cast_count; // probably pending spell cast spell->m_targets = targets; SpellCastResult result = spell->CheckPetCast(NULL); if (result == SPELL_CAST_OK) { pet->AddCreatureSpellCooldown(spellid); if (pet->isPet()) { //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*)pet)->getPetType() == SUMMON_PET && (urand(0, 100) < 10)) pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL); else pet->SendPetAIReaction(guid); } spell->prepare(&(spell->m_targets)); } else { pet->SendPetCastFail(spellid, result); if (!pet->HasSpellCooldown(spellid)) GetPlayer()->SendClearCooldown(spellid, pet); spell->finish(false); delete spell; } }
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); if (!_player->GetPet() && !_player->GetCharm()) return; if (GUID_HIPART(guid) == HIGHGUID_PLAYER) return; Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player,guid); if (!pet || (pet != _player->GetPet() && pet!= _player->GetCharm())) { sLog.outError( "HandlePetCastSpellOpcode: Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); return; } if (pet->GetGlobalCooldown() > 0) return; SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid); if (!spellInfo) { sLog.outError("WORLD: unknown PET spell id %i", spellid); return; } // do not cast not learned spells if (!pet->HasSpell(spellid) || IsPassiveSpell(spellid)) return; SpellCastTargets targets; if (!targets.read(&recvPacket,pet,spellInfo)) return; pet->clearUnitState(UNIT_STAT_FOLLOW); Spell *spell = new Spell(pet, spellInfo, false); spell->m_cast_count = cast_count; // probably pending spell cast spell->m_targets = targets; SpellCastResult result = spell->CheckPetCast(NULL); if (result == SPELL_CAST_OK) { pet->AddCreatureSpellCooldown(spellid); if (pet->isPet()) { //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*)pet)->getPetType() == SUMMON_PET && (urand(0, 100) < 10)) pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL); else pet->SendPetAIReaction(guid); } spell->prepare(&(spell->m_targets)); } else { pet->SendPetCastFail(spellid, result); if (!pet->HasSpellCooldown(spellid)) GetPlayer()->SendClearCooldown(spellid, pet); spell->finish(false); delete spell; } }
void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spellid, uint16 flag, ObjectGuid guid2, float x, float y, float z) { CharmInfo* charmInfo = pet->GetCharmInfo(); if (!charmInfo) { TC_LOG_DEBUG("network", "WorldSession::HandlePetAction(petGuid: %s, tagGuid: %s, spellId: %u, flag: %u): object (%s Entry: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", guid1.ToString().c_str(), guid2.ToString().c_str(), spellid, flag, pet->GetGUID().ToString().c_str(), pet->GetEntry(), pet->GetTypeId()); return; } switch (flag) { case ACT_COMMAND: //0x07 switch (spellid) { case COMMAND_STAY: //flat=1792 //STAY pet->StopMoving(); pet->GetMotionMaster()->Clear(false); pet->GetMotionMaster()->MoveIdle(); charmInfo->SetCommandState(COMMAND_STAY); charmInfo->SetIsCommandAttack(false); charmInfo->SetIsAtStay(true); charmInfo->SetIsCommandFollow(false); 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->SetIsCommandFollow(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; pet->ClearUnitState(UNIT_STATE_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->SetIsCommandFollow(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->SetIsCommandFollow(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; case COMMAND_MOVE_TO: pet->StopMoving(); pet->GetMotionMaster()->Clear(false); pet->GetMotionMaster()->MovePoint(0, x, y, z); charmInfo->SetCommandState(COMMAND_MOVE_TO); charmInfo->SetIsCommandAttack(false); charmInfo->SetIsAtStay(true); charmInfo->SetIsFollowing(false); charmInfo->SetIsReturning(false); charmInfo->SaveStayPosition(); break; default: TC_LOG_ERROR("network", "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(); // no break; 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.IsEmpty()) unit_target = ObjectAccessor::GetUnit(*_player, guid2); // do not cast unknown spells SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid); if (!spellInfo) { TC_LOG_ERROR("network", "WORLD: unknown PET spell id %i", spellid); return; } for (SpellEffectInfo const* effect: spellInfo->GetEffectsForDifficulty(DIFFICULTY_NONE)) { if (effect && (effect->TargetA.GetTarget() == TARGET_UNIT_SRC_AREA_ENEMY || effect->TargetA.GetTarget() == TARGET_UNIT_DEST_AREA_ENEMY || effect->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 (Player* player = unit_target->ToPlayer()) pet->SendUpdateToPlayer(player); } else if (Unit* unit_target2 = spell->m_targets.GetUnitTarget()) { pet->SetInFront(unit_target2); if (Player* player = unit_target2->ToPlayer()) pet->SendUpdateToPlayer(player); } if (Unit* powner = pet->GetCharmerOrOwner()) if (Player* player = powner->ToPlayer()) pet->SendUpdateToPlayer(player); result = SPELL_CAST_OK; } if (result == SPELL_CAST_OK) { 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()) /// @todo: confirm this check Spell::SendCastResult(GetPlayer(), spellInfo, 0, result); else spell->SendPetCastResult(result); if (!pet->GetSpellHistory()->HasCooldown(spellid)) pet->GetSpellHistory()->ResetCooldown(spellid, true); 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: TC_LOG_ERROR("network", "WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid); } }
void WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket) { DETAIL_LOG("WORLD: CMSG_PET_CAST_SPELL"); ObjectGuid guid; uint32 spellid; recvPacket >> guid >> spellid; DEBUG_LOG("WORLD: CMSG_PET_CAST_SPELL, %s, spellid %u", guid.GetString().c_str(), spellid); Creature* pet = _player->GetMap()->GetAnyTypeCreature(guid); if (!pet || (guid != _player->GetPetGuid() && guid != _player->GetCharmGuid())) { sLog.outError("HandlePetCastSpellOpcode: %s isn't pet of %s .", guid.GetString().c_str(), GetPlayer()->GetGuidStr().c_str()); return; } SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellid); if (!spellInfo) { sLog.outError("WORLD: unknown PET spell id %i", spellid); return; } if (pet->GetCharmInfo() && pet->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) return; // do not cast not learned spells if (!pet->HasSpell(spellid) || IsPassiveSpell(spellInfo)) return; SpellCastTargets targets; recvPacket >> targets.ReadForCaster(pet); pet->clearUnitState(UNIT_STAT_MOVING); Spell *spell = new Spell(pet, spellInfo, false); spell->m_targets = targets; SpellCastResult result = spell->CheckPetCast(NULL); if (result == SPELL_CAST_OK) { pet->AddCreatureSpellCooldown(spellid); if (pet->IsPet()) { ((Pet*)pet)->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 (((Pet*)pet)->getPetType() == SUMMON_PET && (urand(0, 100) < 10)) pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL); else pet->SendPetAIReaction(); } spell->prepare(&(spell->m_targets)); } else { pet->SendPetCastFail(spellid, result); if (!pet->HasSpellCooldown(spellid)) GetPlayer()->SendClearCooldown(spellid, pet); spell->finish(false); delete spell; } }
void WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket) { DETAIL_LOG("WORLD: CMSG_PET_CAST_SPELL"); ObjectGuid guid; uint32 spellid; recvPacket >> guid >> spellid; DEBUG_LOG("WORLD: CMSG_PET_CAST_SPELL, %s, spellid %u", guid.GetString().c_str(), spellid); Unit* petUnit = _player->GetMap()->GetUnit(guid); if (!petUnit || (guid != _player->GetPetGuid() && guid != _player->GetCharmGuid())) { sLog.outError("HandlePetSpellAutocastOpcode. %s isn't pet of %s .", guid.GetString().c_str(), _player->GetGuidStr().c_str()); return; } //TODO: all those typecasting are probably not needed anymore Creature* petCreature = petUnit->GetTypeId() == TYPEID_UNIT ? static_cast<Creature*>(petUnit) : nullptr; Pet* pet = (petCreature && petCreature->IsPet()) ? static_cast<Pet*>(petUnit) : nullptr; SpellEntry const* spellInfo = sSpellTemplate.LookupEntry<SpellEntry>(spellid); if (!spellInfo) { sLog.outError("WORLD: unknown PET spell id %i", spellid); return; } if (!petUnit->IsSpellReady(*spellInfo)) return; // do not cast not learned spells if (!petUnit->HasSpell(spellid) || IsPassiveSpell(spellInfo)) return; SpellCastTargets targets; recvPacket >> targets.ReadForCaster(petUnit); petUnit->clearUnitState(UNIT_STAT_MOVING); Spell* spell = new Spell(petUnit, spellInfo, false); spell->m_targets = targets; SpellCastResult result = spell->CheckPetCast(nullptr); if (result == SPELL_CAST_OK) { if (pet) pet->CheckLearning(spellid); spell->SpellStart(&(spell->m_targets)); } else { Unit* owner = petCreature ? petCreature->GetMaster() : nullptr; if (owner && owner->GetTypeId() == TYPEID_PLAYER) { Spell::SendCastResult((Player*)owner, spellInfo, 0, result, true); if (petUnit->IsSpellReady(*spellInfo)) GetPlayer()->SendClearCooldown(spellid, petUnit); } spell->finish(false); delete spell; } }