Exemplo n.º 1
0
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 (GUID: %u 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->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;
        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)
            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 (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
        {
            if (spellInfo->Effects[i].TargetA.GetTarget() == TARGET_UNIT_SRC_AREA_ENEMY || spellInfo->Effects[i].TargetA.GetTarget() == TARGET_UNIT_DEST_AREA_ENEMY || spellInfo->Effects[i].TargetA.GetTarget() == TARGET_DEST_DYNOBJ_ENEMY)
                return;
        }

        // do not cast not learned spells
        if (!pet->HasSpell(spellid) || spellInfo->IsPassive())
            return;

        //  Clear the flags as if owner clicked 'attack'. AI will reset them
        //  after AttackStart, even if spell failed
        if (pet->GetCharmInfo())
        {
            pet->GetCharmInfo()->SetIsAtStay(false);
            pet->GetCharmInfo()->SetIsCommandAttack(true);
            pet->GetCharmInfo()->SetIsReturning(false);
            pet->GetCharmInfo()->SetIsFollowing(false);
        }

        Spell* spell = new Spell(pet, spellInfo, TRIGGERED_NONE);

        SpellCastResult result = spell->CheckPetCast(unit_target);

        //auto turn to target unless possessed
        if (result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->isPossessed() && !pet->IsVehicle())
        {
            if (unit_target)
            {
                pet->SetInFront(unit_target);
                if (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() && !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->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);
    }
}