Пример #1
0
void PetAI::Reset()
{
    m_primaryTargetGuid.Clear();
    m_savedTargetGuid.Clear();
    m_attackDistanceRecheckTimer.SetInterval(TIME_INTERVAL_LOOK);
    m_attackDistanceRecheckTimer.Reset();
    m_updateAlliesTimer.SetInterval(ALLIES_UPDATE_TIME);
    m_updateAlliesTimer.Reset();
    UpdateAllies();

    for (uint8 i = PET_SPELL_PASSIVE; i < PET_SPELL_MAX; ++i)
        m_spellType[i].clear();

    m_AIType = PET_AI_PASSIVE;
    m_attackDistance  = 0.0f;
    float f_range   = 0.0f;

    if (!m_creature->GetCharmInfo())
        return;

    uint32 spellsSize = m_creature->IsPet() ? ((Pet*)m_creature)->GetPetAutoSpellSize() : m_creature->GetPetAutoSpellSize();
    uint8 rangedDamageSpells = 0;
    uint8 meleeDamageSpells = 0;

    // classification for pet spells
    for (uint32 i = 0; i < spellsSize; ++i)
    {
        uint32 spellID = m_creature->IsPet() ? ((Pet*)m_creature)->GetPetAutoSpellOnPos(i) : m_creature->GetPetAutoSpellOnPos(i);
        if (!spellID)
            continue;

        SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellID);
        if (!spellInfo)
            continue;

        if (IsPassiveSpell(spellInfo))
        {
            m_spellType[PET_SPELL_PASSIVE].insert(spellID);
            continue;
        }

        if (IsNonCombatSpell(spellInfo))
        {
            // Voidwalker Consume Shadows
            if (IsChanneledSpell(spellInfo))
                m_spellType[PET_SPELL_HEAL].insert(spellID);
            else
                m_spellType[PET_SPELL_NONCOMBAT].insert(spellID);
            continue;
        }

        // need more correct define this type
        if (IsSpellReduceThreat(spellInfo) || IsChanneledSpell(spellInfo))
        {
            m_spellType[PET_SPELL_DEFENCE].insert(spellID);
            continue;
        }

        // Voracious Appetite && Cannibalize && Carrion Feeder
        if (spellInfo->HasAttribute(SPELL_ATTR_ABILITY) && spellInfo->HasAttribute(SPELL_ATTR_EX2_ALLOW_DEAD_TARGET))
        {
            m_spellType[PET_SPELL_HEAL].insert(spellID);
            continue;
        }

        if (IsPositiveSpell(spellInfo) && IsSpellAppliesAura(spellInfo))
        {
            m_spellType[PET_SPELL_BUFF].insert(spellID);
            continue;
        }

        if (spellInfo->HasAttribute(SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY))
        {
            m_spellType[PET_SPELL_FREEACTION].insert(spellID);
            continue;
        }

        // don't have SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY !
        if (spellInfo->HasAttribute(SPELL_ATTR_EX_CANT_REFLECTED) ||
                spellInfo->HasAttribute(SPELL_ATTR_EX7_HAS_CHARGE_EFFECT))
        {
            m_spellType[PET_SPELL_ATTACKSTART].insert(spellID);
            continue;
        }

        if (IsSpellIncreaseThreat(spellInfo))
        {
            m_spellType[PET_SPELL_THREAT].insert(spellID);
            continue;
        }

        // all non-combat spells classified.
        switch (spellInfo->GetRangeIndex())
        {
        case SPELL_RANGE_IDX_COMBAT:
        {
            if (IsSpellCauseDamage(spellInfo))
            {
                m_spellType[PET_SPELL_MELEE].insert(spellID);
                ++meleeDamageSpells;
            }
            else
            {
                m_spellType[PET_SPELL_SPECIAL].insert(spellID);
            }
            break;
        }
        // possible debuffs or auras?
        case SPELL_RANGE_IDX_SELF_ONLY:
        case SPELL_RANGE_IDX_ANYWHERE:
        {
            m_spellType[PET_SPELL_SPECIAL].insert(spellID);
            break;
        }
        default:
        {
            float range = GetSpellMaxRange(sSpellRangeStore.LookupEntry(spellInfo->GetRangeIndex()), false);
            if (f_range < M_NULL_F || (range > M_NULL_F && range < f_range))
                f_range = range;
            if (IsSpellCauseDamage(spellInfo))
            {
                m_spellType[PET_SPELL_RANGED].insert(spellID);
                ++rangedDamageSpells;
            }
            else
            {
                m_spellType[PET_SPELL_SPECIAL].insert(spellID);
            }
            break;
        }
        }
    }

    // define initial AI type
    if (m_creature->IsVehicle())
        m_AIType = PET_AI_PASSIVE;

    if (m_spellType[PET_SPELL_RANGED].size() > 0 && (m_spellType[PET_SPELL_MELEE].size() < m_spellType[PET_SPELL_RANGED].size()))
    {
        m_AIType = PET_AI_RANGED;
        m_attackDistance = f_range - m_creature->GetObjectBoundingRadius() - 2.0f;
        if (m_attackDistance < 20.0f)
            m_attackDistance = 18.0f;
    }
    else
    {
        m_AIType = PET_AI_MELEE;
        m_attackDistance = 0.0f;
    }
    m_savedAIType = m_AIType;

    m_creature->GetMotionMaster()->MoveTargetedHome();

    DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS,"PetAI::Reset %s, AI %u dist %f, spells: "SIZEFMTD" "SIZEFMTD" "SIZEFMTD" "SIZEFMTD" "SIZEFMTD" "SIZEFMTD" "SIZEFMTD" "SIZEFMTD" "SIZEFMTD" "SIZEFMTD" "SIZEFMTD" "SIZEFMTD ,
                     m_creature->GetObjectGuid().GetString().c_str(),
                     m_AIType,
                     m_attackDistance,
                     m_spellType[PET_SPELL_PASSIVE].size(),
                     m_spellType[PET_SPELL_NONCOMBAT].size(),
                     m_spellType[PET_SPELL_BUFF].size(),
                     m_spellType[PET_SPELL_DEBUFF].size(),
                     m_spellType[PET_SPELL_FREEACTION].size(),
                     m_spellType[PET_SPELL_ATTACKSTART].size(),
                     m_spellType[PET_SPELL_THREAT].size(),
                     m_spellType[PET_SPELL_MELEE].size(),
                     m_spellType[PET_SPELL_RANGED].size(),
                     m_spellType[PET_SPELL_DEFENCE].size(),
                     m_spellType[PET_SPELL_SPECIAL].size(),
                     m_spellType[PET_SPELL_HEAL].size()
                    );
}
Пример #2
0
void PetAI::UpdateAI(const uint32 diff)
{
    if (!m_creature->isAlive())
        return;

    Unit* owner = m_creature->GetCharmerOrOwner();

    if (m_updateAlliesTimer <= diff)
        // UpdateAllies self set update timer
        UpdateAllies();
    else
        m_updateAlliesTimer -= diff;

    if (inCombat && (!m_creature->getVictim() || (m_creature->IsPet() && ((Pet*)m_creature)->GetModeFlags() & PET_MODE_DISABLE_ACTIONS)))
        _stopAttack();

    // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc.
    if (m_creature->getVictim())
    {
        if (_needToStop())
        {
            DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "PetAI (guid = %u) is stopping attack.", m_creature->GetGUIDLow());
            _stopAttack();
            return;
        }

        bool meleeReach = m_creature->CanReachWithMeleeAttack(m_creature->getVictim());

        if (m_creature->IsStopped() || meleeReach)
        {
            // required to be stopped cases
            if (m_creature->IsStopped() && m_creature->IsNonMeleeSpellCasted(false))
            {
                if (m_creature->hasUnitState(UNIT_STAT_FOLLOW_MOVE))
                    m_creature->InterruptNonMeleeSpells(false);
                else
                    return;
            }
            // not required to be stopped case
            else if (DoMeleeAttackIfReady())
            {
                if (!m_creature->getVictim())
                    return;

                //if pet misses its target, it will also be the first in threat list
                m_creature->getVictim()->AddThreat(m_creature);

                if (_needToStop())
                    _stopAttack();
            }
        }
    }
    else if (owner && m_creature->GetCharmInfo())
    {
        if (owner->isInCombat() && !(m_creature->GetCharmInfo()->HasReactState(REACT_PASSIVE) || m_creature->GetCharmInfo()->HasCommandState(COMMAND_STAY)))
        {
            AttackStart(owner->getAttackerForHelper());
        }
        else if(m_creature->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))
        {
            if (!m_creature->hasUnitState(UNIT_STAT_FOLLOW) )
            {
                m_creature->GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE);
            }
        }
    }

    // Autocast (casted only in combat or persistent spells in any state)
    if (!m_creature->IsNonMeleeSpellCasted(false))
    {
        typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList;
        TargetSpellList targetSpellStore;

        for (uint8 i = 0; i < m_creature->GetPetAutoSpellSize(); ++i)
        {
            uint32 spellID = m_creature->GetPetAutoSpellOnPos(i);
            if (!spellID)
                continue;

            SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID);
            if (!spellInfo)
                continue;

            if (m_creature->GetCharmInfo() && m_creature->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo))
                continue;

            // ignore some combinations of combat state and combat/noncombat spells
            if (!inCombat)
            {
                // ignore attacking spells, and allow only self/around spells
                if (!IsPositiveSpell(spellInfo->Id))
                    continue;

                // non combat spells allowed
                // only pet spells have IsNonCombatSpell and not fit this reqs:
                // Consume Shadows, Lesser Invisibility, so ignore checks for its
                if (!IsNonCombatSpell(spellInfo))
                {
                    // allow only spell without spell cost or with spell cost but not duration limit
                    int32 duration = GetSpellDuration(spellInfo);
                    if ((spellInfo->manaCost || spellInfo->ManaCostPercentage || spellInfo->manaPerSecond) && duration > 0)
                        continue;

                    // allow only spell without cooldown > duration
                    int32 cooldown = GetSpellRecoveryTime(spellInfo);
                    if (cooldown >= 0 && duration >= 0 && cooldown > duration)
                        continue;
                }
            }
            else
            {
                // just ignore non-combat spells
                if (IsNonCombatSpell(spellInfo))
                    continue;
            }

            Spell *spell = new Spell(m_creature, spellInfo, false);

            if (inCombat && !m_creature->hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(m_creature->getVictim()))
            {
                targetSpellStore.push_back(TargetSpellList::value_type(m_creature->getVictim(), spell));
                continue;
            }
            else
            {
                bool spellUsed = false;
                for (AllySet::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar)
                {
                    Unit* Target = m_creature->GetMap()->GetUnit(*tar);

                    //only buff targets that are in combat, unless the spell can only be cast while out of combat
                    if (!Target)
                        continue;

                    if (spell->CanAutoCast(Target))
                    {
                        targetSpellStore.push_back(TargetSpellList::value_type(Target, spell));
                        spellUsed = true;
                        break;
                    }
                }
                if (!spellUsed)
                    delete spell;
            }
        }

        //found units to cast on to
        if (!targetSpellStore.empty())
        {
            uint32 index = urand(0, targetSpellStore.size() - 1);

            Spell* spell  = targetSpellStore[index].second;
            Unit*  target = targetSpellStore[index].first;

            targetSpellStore.erase(targetSpellStore.begin() + index);

            SpellCastTargets targets;
            targets.setUnitTarget( target );

            if (!m_creature->HasInArc(M_PI_F, target))
            {
                m_creature->SetInFront(target);
                if (target->GetTypeId() == TYPEID_PLAYER)
                    m_creature->SendCreateUpdateToPlayer((Player*)target);

                if (owner && owner->GetTypeId() == TYPEID_PLAYER)
                    m_creature->SendCreateUpdateToPlayer( (Player*)owner );
            }

            m_creature->AddCreatureSpellCooldown(spell->m_spellInfo->Id);

            spell->prepare(&targets);
        }

        // deleted cached Spell objects
        for(TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr)
            delete itr->second;
    }
}
Пример #3
0
void PetAI::UpdateAI(const uint32 diff)
{
    if (!m_creature->isAlive())
        return;

    m_updateAlliesTimer.Update(diff);
    if (m_updateAlliesTimer.Passed())
    {
        UpdateAllies();
        m_updateAlliesTimer.Reset();
    }

    if (!inCombat && m_savedTargetGuid)
    {
        if (Unit* savedTarget = m_creature->GetMap()->GetUnit(m_savedTargetGuid))
        {
            if (!savedTarget->isAlive())
                m_savedTargetGuid.Clear();
            else if (!savedTarget->IsCrowdControlled())
                AttackStart(savedTarget);
        }
        else
            m_savedTargetGuid.Clear();
    }

    Unit* pVictim = m_creature->getVictim();

    if (inCombat &&
            (!pVictim || !pVictim->isAlive() ||
             (m_creature->IsPet() && m_creature->GetCharmInfo()->HasState(CHARM_STATE_ACTION, ACTIONS_DISABLE))))
        _stopAttack();

    if (m_creature->hasUnitState(UNIT_STAT_CAN_NOT_REACT) || m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED))
    {
        UpdateAIType();
        return;
    }

    Unit* owner = m_creature->GetCharmerOrOwner();

    // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc.
    if (pVictim)
    {
        bool meleeReach = m_creature->CanReachWithMeleeAttack(pVictim);

        if (_needToStop())
        {
            DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "PetAI (guid = %u) is stopping attack.", m_creature->GetGUIDLow());
            _stopAttack();
            return;
        }
        else if (!pVictim->isAlive())                        // Stop attack if target dead
        {
            m_creature->InterruptNonMeleeSpells(false);
            _stopAttack();
            return;
        }
        // Stop attack if target under CC effect
        else if (sWorld.getConfig(CONFIG_BOOL_PET_ADVANCED_AI) && IsInCombat() &&
                 pVictim->IsCrowdControlled() &&
                 !m_creature->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
        {
            m_savedTargetGuid = pVictim->GetObjectGuid();
            m_creature->InterruptSpell(CURRENT_GENERIC_SPELL, true);
            if (!m_creature->IsNonMeleeSpellCasted(false, false, true))
                _stopAttack();
            return;
        }
        else if (m_creature->IsStopped() || meleeReach)
        {
            // required to be stopped cases
            if (m_creature->IsStopped() && m_creature->IsNonMeleeSpellCasted(false))
            {
                if (m_creature->hasUnitState(UNIT_STAT_FOLLOW_MOVE))
                    m_creature->InterruptNonMeleeSpells(false);
                else
                    return;
            }
            // not required to be stopped case
            else if (DoMeleeAttackIfReady())
            {
                if (pVictim = m_creature->getVictim())
                {
                    // if pet misses its target, it will also be the first in threat list
                    pVictim->AddThreat(m_creature);

                    if (_needToStop())
                        _stopAttack();
                }
            }
        }

        if (!m_creature->IsNonMeleeSpellCasted(true))
        {
            m_attackDistanceRecheckTimer.Update(diff);
            if (m_attackDistanceRecheckTimer.Passed())
            {
                m_attackDistanceRecheckTimer.Reset();
                if (sWorld.getConfig(CONFIG_BOOL_PET_ADVANCED_AI) && m_AIType == PET_AI_RANGED && pVictim)
                {
                    float dist = m_creature->GetDistance(pVictim);
                    if ((m_creature->CanReachWithMeleeAttack(pVictim) &&
                            m_creature->IsWithinDist(owner, m_creature->GetMap()->GetVisibilityDistance() / 2.0f)) ||
                            dist > (m_attackDistance + 2.0f))
                    {
                        MoveToVictim(pVictim);
                        return;
                    }
                }

                if (sWorld.getConfig(CONFIG_BOOL_PET_ADVANCED_AI))
                {
                    // FIXME: AOE check
                }
            }
        }
    }
    else if (Unit* target = GetPrimaryTarget())
    {
        AttackStart(target);
    }
    else if (owner && owner->IsInCombat())
    {
        switch (m_creature->GetCharmState(CHARM_STATE_REACT))
        {
        case REACT_DEFENSIVE:
        {
            if (!pVictim || !pVictim->isAlive() ||
                    (!m_primaryTargetGuid && owner->getVictim() != pVictim && owner->getVictim()->isAlive()))
                AttackStart(owner->getAttackerForHelper());
            break;
        }
        case REACT_AGGRESSIVE:
        {
            if (!pVictim || !pVictim->isAlive())
                AttackStart(owner->getAttackerForHelper());
            break;
        }
        case REACT_PASSIVE:
        default:
            break;
        }
    }

    UpdateAIType();

    if (m_creature->IsNonMeleeSpellCasted(true))
        return;

    // Autocast (casted only in combat or persistent spells in any state)
    if (!sWorld.getConfig(CONFIG_BOOL_PET_ADVANCED_AI) && m_AIType != PET_AI_PASSIVE)
    {
        typedef std::vector<std::pair<ObjectGuid, uint32> > TargetSpellList;
        TargetSpellList targetSpellStore;

        for (uint8 i = 0; i < m_creature->GetPetAutoSpellSize(); ++i)
        {
            uint32 spellID = m_creature->GetPetAutoSpellOnPos(i);
            if (!spellID)
                continue;

            SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellID);
            if (!spellInfo)
                continue;

            if (m_creature->GetCharmInfo() && m_creature->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo))
                continue;

            if (m_creature->HasSpellCooldown(spellInfo))
                continue;

            // ignore some combinations of combat state and combat/noncombat spells
            if (!inCombat)
            {
                // ignore attacking spells, and allow only self/around spells
                if (!IsPositiveSpell(spellInfo->Id))
                    continue;

                // non combat spells allowed
                // only pet spells have IsNonCombatSpell and not fit this reqs:
                // Consume Shadows, Lesser Invisibility, so ignore checks for its
                if (!IsNonCombatSpell(spellInfo))
                {
                    // allow only spell without spell cost or with spell cost but not duration limit
                    int32 duration = GetSpellDuration(spellInfo);
                    if ((spellInfo->manaCost || spellInfo->ManaCostPercentage || spellInfo->manaPerSecond) && duration > 0)
                        continue;

                    // allow only spell without cooldown > duration
                    int32 cooldown = GetSpellRecoveryTime(spellInfo);
                    if (cooldown >= 0 && duration >= 0 && cooldown > duration)
                        continue;
                }
            }
            else
            {
                // just ignore non-combat spells
                if (IsNonCombatSpell(spellInfo))
                    continue;
            }

            Unit* autoCastTarget = NULL;

            if (inCombat && m_creature->getVictim() && !m_creature->hasUnitState(UNIT_STAT_FOLLOW))
            {
                SpellCastResult result = CanAutoCast(m_creature->getVictim(), spellInfo);
                if (result == SPELL_CAST_OK || result == SPELL_FAILED_UNIT_NOT_INFRONT)
                    autoCastTarget = m_creature->getVictim();
            }

            if (!autoCastTarget)
            {
                for (GuidSet::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar)
                {
                    Unit* target = m_creature->GetMap()->GetUnit(*tar);

                    // Only buff targets that are in combat, unless the spell can only be cast while out of combat
                    if (!target)
                        continue;

                    SpellCastResult result = CanAutoCast(target, spellInfo);
                    if (result == SPELL_CAST_OK || result == SPELL_FAILED_UNIT_NOT_INFRONT)
                    {
                        autoCastTarget = target;
                        break;
                    }
                }
            }

            if (autoCastTarget)
                targetSpellStore.push_back(TargetSpellList::value_type(autoCastTarget->GetObjectGuid(), spellInfo->Id));
        }

        // found units to cast on to
        if (!targetSpellStore.empty())
        {
            uint32 index = urand(0, targetSpellStore.size() - 1);
            if (Unit* target = m_creature->GetMap()->GetUnit(targetSpellStore[index].first))
                m_creature->DoPetCastSpell(target, targetSpellStore[index].second);
        }
    }
    else
    {
        AutoSpellList currentSpells;
        switch (m_AIType)
        {
        case PET_AI_PASSIVE:
        {
            currentSpells.push_back(GetSpellType(PET_SPELL_BUFF));
            break;
        }
        case PET_AI_SLACKER:
        {
            if (!IsInCombat())
                break;

            if (m_creature->IsCrowdControlled() || (owner && owner->IsCrowdControlled()))
                currentSpells.push_back(GetSpellType(PET_SPELL_FREEACTION));
            currentSpells.push_back(GetSpellType(PET_SPELL_DEFENCE));
            currentSpells.push_back(GetSpellType(PET_SPELL_BUFF));
            currentSpells.push_back(GetSpellType(PET_SPELL_DEBUFF));
            currentSpells.push_back(GetSpellType(PET_SPELL_RANGED));
            break;
        }
        case PET_AI_HEALER:
        {
            if (!IsInCombat())
                break;

            if (m_creature->IsCrowdControlled() || (owner && owner->IsCrowdControlled()))
                currentSpells.push_back(GetSpellType(PET_SPELL_FREEACTION));
            if (m_creature->GetHealth() < m_creature->GetMaxHealth() ||
                    (owner && (owner->GetHealth() < owner->GetMaxHealth())))
                currentSpells.push_back(GetSpellType(PET_SPELL_HEAL));
            currentSpells.push_back(GetSpellType(PET_SPELL_BUFF));
            currentSpells.push_back(GetSpellType(PET_SPELL_RANGED));
            break;
        }
        case PET_AI_RANGED:
        {
            if (!IsInCombat())
                break;

            if (m_creature->IsCrowdControlled() || (owner && owner->IsCrowdControlled()))
                currentSpells.push_back(GetSpellType(PET_SPELL_FREEACTION));
            currentSpells.push_back(GetSpellType(PET_SPELL_RANGED));
            currentSpells.push_back(GetSpellType(PET_SPELL_DEBUFF));
            currentSpells.push_back(GetSpellType(PET_SPELL_BUFF));
            break;
        }
        case PET_AI_MELEE:
        case PET_AI_RANGED_NOAMMO:
        {
            if (!IsInCombat())
                break;

            if (Unit* victim = m_creature->getVictim())
            {
                if (!victim->getVictim() || (victim->getVictim()->GetObjectGuid() != m_creature->GetObjectGuid()))
                {
                    currentSpells.push_back(GetSpellType(PET_SPELL_ATTACKSTART));
                    currentSpells.push_back(GetSpellType(PET_SPELL_THREAT));
                }
            }

            if (m_creature->IsCrowdControlled() || (owner && owner->IsCrowdControlled()))
                currentSpells.push_back(GetSpellType(PET_SPELL_FREEACTION));
        }
        /* no break here!*/
        default:
        {
            if (!IsInCombat())
                break;

            currentSpells.push_back(GetSpellType(PET_SPELL_MELEE));
            currentSpells.push_back(GetSpellType(PET_SPELL_DEBUFF));
            currentSpells.push_back(GetSpellType(PET_SPELL_RANGED));
            currentSpells.push_back(GetSpellType(PET_SPELL_BUFF));
            break;
        }
        }

        if (!IsInCombat())
        {
            currentSpells.push_back(GetSpellType(PET_SPELL_NONCOMBAT));
            if (m_creature->GetHealthPercent() < 95.0f)
                currentSpells.push_back(GetSpellType(PET_SPELL_HEAL));
        }
        else
            currentSpells.push_back(GetSpellType(PET_SPELL_SPECIAL));

        for (AutoSpellList::const_iterator itr = currentSpells.begin(); itr != currentSpells.end(); ++itr)
        {
            uint32 spellID = *itr;
            if (!spellID)
                continue;

            SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellID);
            if (!spellInfo)
                continue;

            if (m_creature->GetCharmInfo() && m_creature->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo))
                continue;

            Unit* pTarget = m_creature->IsPet()
                            ? ((Pet*)m_creature)->SelectPreferredTargetForSpell(spellInfo)
                            : ((Creature*)m_creature)->SelectPreferredTargetForSpell(spellInfo);

            bool b_castOk = false;

            if (pTarget)
            {
                SpellCastResult result = CanAutoCast(pTarget, spellInfo);
                DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS,"PetAI::Update %s, AI %u try cast %u Target %s",
                                 m_creature->GetGuidStr().c_str(),
                                 m_AIType,
                                 spellID,
                                 pTarget ? pTarget->GetGuidStr().c_str() : "<none>");

                switch (result)
                {
                case SPELL_FAILED_TOO_CLOSE:
                case SPELL_FAILED_OUT_OF_RANGE:
                    break; // ignore
                case SPELL_FAILED_UNIT_NOT_INFRONT:
                {
                    if (DoCastSpellIfCan(pTarget, spellID) == CAST_OK)
                    {
                        b_castOk = true;
                        m_creature->SetInFront(pTarget);
                        if (pTarget->GetTypeId() == TYPEID_PLAYER)
                            m_creature->SendCreateUpdateToPlayer((Player*)pTarget);
                    }
                    break;
                }
                case SPELL_CAST_OK:
                {
                    if (DoCastSpellIfCan(pTarget, spellID) == CAST_OK)
                        b_castOk = true;

                    break;
                }
                default:
                {
                    Player* owner = (Player*)m_creature->GetOwner();
                    if (owner)
                        Spell::SendCastResult(owner, spellInfo, 0, result, true);

                    DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "PetAI::Update cast %s, AI %u Target %s spell %u result %u",
                                     m_creature->GetGuidStr().c_str(),
                                     m_AIType,
                                     pTarget ? pTarget->GetGuidStr().c_str() : "<none>",
                                     spellID,
                                     result);
                    break;
                }
                }
            }
            else
                continue;

            if (b_castOk)
            {
                if (m_creature->IsPet())
                {
                    if (((Pet*)m_creature)->getPetType() == SUMMON_PET && (urand(0, 100) < 10))
                        m_creature->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL);
                    else
                        m_creature->SendPetAIReaction();
                }
                break;
            }
        }
    }
}
Пример #4
0
void PetAI::UpdateAI(const uint32 diff)
{
    if (!i_pet.isAlive())
        return;

    Unit* owner = i_pet.GetCharmerOrOwner();

    if(m_updateAlliesTimer <= diff)
        // UpdateAllies self set update timer
        UpdateAllies();
    else
        m_updateAlliesTimer -= diff;

    if (inCombat && !i_pet.getVictim())
        _stopAttack();

    // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc.
    if( i_pet.getVictim() )
    {
        if( _needToStop() )
        {
            DEBUG_LOG("Pet AI stoped attacking [guid=%u]", i_pet.GetGUIDLow());
            _stopAttack();
            return;
        }
        else if( i_pet.IsStopped() || i_pet.IsWithinDistInMap(i_pet.getVictim(), ATTACK_DISTANCE))
        {
            // required to be stopped cases
            if ( i_pet.IsStopped() && i_pet.IsNonMeleeSpellCasted(false) )
            {
                if( i_pet.hasUnitState(UNIT_STAT_FOLLOW) )
                    i_pet.InterruptNonMeleeSpells(false);
                else
                    return;
            }
            // not required to be stopped case
            else if( i_pet.isAttackReady() && i_pet.canReachWithAttack(i_pet.getVictim()) )
            {
                i_pet.AttackerStateUpdate(i_pet.getVictim());

                i_pet.resetAttackTimer();

                if ( !i_pet.getVictim() )
                    return;

                //if pet misses its target, it will also be the first in threat list
                i_pet.getVictim()->AddThreat(&i_pet,0.0f);

                if( _needToStop() )
                    _stopAttack();
            }
        }
    }
    else if(owner && i_pet.GetCharmInfo())
    {
        if(owner->isInCombat() && !(i_pet.GetCharmInfo()->HasReactState(REACT_PASSIVE) || i_pet.GetCharmInfo()->HasCommandState(COMMAND_STAY)))
        {
            AttackStart(owner->getAttackerForHelper());
        }
        else if(i_pet.GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))
        {
            if (!i_pet.hasUnitState(UNIT_STAT_FOLLOW) )
            {
                i_pet.GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE);
            }
        }
    }

    if (i_pet.GetGlobalCooldown() == 0 && !i_pet.IsNonMeleeSpellCasted(false))
    {
        //Autocast
        for (uint8 i = 0; i < i_pet.GetPetAutoSpellSize(); i++)
        {
            uint32 spellID = i_pet.GetPetAutoSpellOnPos(i);
            if (!spellID)
                continue;

            SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID);
            if (!spellInfo)
                continue;

            // ignore some combinations of combat state and combat/noncombat spells
            if (!inCombat)
            {
                if (!IsPositiveSpell(spellInfo->Id))
                    continue;
            }
            else
            {
                if (IsNonCombatSpell(spellInfo))
                    continue;
            }

            Spell *spell = new Spell(&i_pet, spellInfo, false, 0);

            if(inCombat && !i_pet.hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(i_pet.getVictim()))
            {
                m_targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(i_pet.getVictim(), spell));
                continue;
            }
            else
            {
                bool spellUsed = false;
                for(std::set<uint64>::iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar)
                {
                    Unit* Target = ObjectAccessor::GetUnit(i_pet,*tar);

                    //only buff targets that are in combat, unless the spell can only be cast while out of combat
                    if(!Target)
                        continue;

                    if(spell->CanAutoCast(Target))
                    {
                        m_targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(Target, spell));
                        spellUsed = true;
                        break;
                    }
                }
                if (!spellUsed)
                    delete spell;
            }
        }

        //found units to cast on to
        if(!m_targetSpellStore.empty())
        {
            uint32 index = urand(0, m_targetSpellStore.size() - 1);

            Spell* spell  = m_targetSpellStore[index].second;
            Unit*  target = m_targetSpellStore[index].first;

            m_targetSpellStore.erase(m_targetSpellStore.begin() + index);

            SpellCastTargets targets;
            targets.setUnitTarget( target );

            if( !i_pet.HasInArc(M_PI, target) )
            {
                i_pet.SetInFront(target);
                if( target->GetTypeId() == TYPEID_PLAYER )
                    i_pet.SendUpdateToPlayer( (Player*)target );

                if(owner && owner->GetTypeId() == TYPEID_PLAYER)
                    i_pet.SendUpdateToPlayer( (Player*)owner );
            }

            i_pet.AddCreatureSpellCooldown(spell->m_spellInfo->Id);
            if(i_pet.isPet())
                ((Pet*)&i_pet)->CheckLearning(spell->m_spellInfo->Id);

            spell->prepare(&targets);
        }
        while (!m_targetSpellStore.empty())
        {
            delete m_targetSpellStore.begin()->second;
            m_targetSpellStore.erase(m_targetSpellStore.begin());
        }
    }
}
Пример #5
0
void PetAI::UpdateAI(const uint32 diff)
{
    if (!me->isAlive())
        return;

    Unit* owner = me->GetCharmerOrOwner();

    if (m_updateAlliesTimer <= diff)
        // UpdateAllies self set update timer
        UpdateAllies();
    else
        m_updateAlliesTimer -= diff;

    // me->getVictim() can't be used for check in case stop fighting, me->getVictim() clear at Unit death etc.
    if (me->getVictim())
    {
        if (_needToStop())
        {
            sLog->outStaticDebug("Pet AI stopped attacking [guid=%u]", me->GetGUIDLow());
            _stopAttack();
            return;
        }
        targetHasCC = _CheckTargetCC(me->getVictim());

        DoMeleeAttackIfReady();
    }
    else if (owner && me->GetCharmInfo()) //no victim
    {
        Unit *nextTarget = SelectNextTarget();

        if (me->HasReactState(REACT_PASSIVE))
            _stopAttack();
        else if (nextTarget)
            AttackStart(nextTarget);
        else
            HandleReturnMovement();
    }
    else if (owner && !me->HasUnitState(UNIT_STAT_FOLLOW)) // no charm info and no victim
        me->GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST, me->GetFollowAngle());

    if (!me->GetCharmInfo())
        return;

    // Autocast (casted only in combat or persistent spells in any state)
    if (!me->HasUnitState(UNIT_STAT_CASTING))
    {
        typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList;
        TargetSpellList targetSpellStore;

        for (uint8 i = 0; i < me->GetPetAutoSpellSize(); ++i)
        {
            uint32 spellID = me->GetPetAutoSpellOnPos(i);
            if (!spellID)
                continue;

            SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID);
            if (!spellInfo)
                continue;

            if (me->GetCharmInfo() && me->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo))
                continue;

            // ignore some combinations of combat state and combat/noncombat spells
            if (!me->getVictim())
            {
                // ignore attacking spells, and allow only self/around spells
                if (!IsPositiveSpell(spellInfo->Id))
                    continue;

                // non combat spells allowed
                // only pet spells have IsNonCombatSpell and not fit this reqs:
                // Consume Shadows, Lesser Invisibility, so ignore checks for its
                if (!IsNonCombatSpell(spellInfo))
                {
                    // allow only spell without spell cost or with spell cost but not duration limit
                    int32 duration = GetSpellDuration(spellInfo);
                    if ((spellInfo->manaCost || spellInfo->ManaCostPercentage || spellInfo->manaPerSecond) && duration > 0)
                        continue;

                    // allow only spell without cooldown > duration
                    int32 cooldown = GetSpellRecoveryTime(spellInfo);
                    if (cooldown >= 0 && duration >= 0 && cooldown > duration)
                        continue;
                }
            }
            else
            {
                // just ignore non-combat spells
                if (IsNonCombatSpell(spellInfo))
                    continue;
            }

            Spell *spell = new Spell(me, spellInfo, false, 0);

            // Fix to allow pets on STAY to autocast
            if (me->getVictim() && _CanAttack(me->getVictim()) && spell->CanAutoCast(me->getVictim()))
            {
                targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(me->getVictim(), spell));
                continue;
            }
            else
            {
                bool spellUsed = false;
                for (std::set<uint64>::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar)
                {
                    Unit* Target = ObjectAccessor::GetUnit(*me,*tar);

                    //only buff targets that are in combat, unless the spell can only be cast while out of combat
                    if (!Target)
                        continue;

                    if (spell->CanAutoCast(Target))
                    {
                        targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(Target, spell));
                        spellUsed = true;
                        break;
                    }
                }
                if (!spellUsed)
                    delete spell;
            }
        }

        //found units to cast on to
        if (!targetSpellStore.empty())
        {
            uint32 index = urand(0, targetSpellStore.size() - 1);

            Spell* spell  = targetSpellStore[index].second;
            Unit*  target = targetSpellStore[index].first;

            targetSpellStore.erase(targetSpellStore.begin() + index);

            SpellCastTargets targets;
            targets.setUnitTarget(target);

            if (!me->HasInArc(M_PI, target))
            {
                me->SetInFront(target);
                if (target && target->GetTypeId() == TYPEID_PLAYER)
                    me->SendUpdateToPlayer(target->ToPlayer());

                if (owner && owner->GetTypeId() == TYPEID_PLAYER)
                    me->SendUpdateToPlayer(owner->ToPlayer());
            }

            me->AddCreatureSpellCooldown(spell->m_spellInfo->Id);

            spell->prepare(&targets);
        }

        // deleted cached Spell objects
        for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr)
            delete itr->second;
    }
}
Пример #6
0
void WorldSession::HandleCancelAuraOpcode(WorldPacket& recvPacket)
{
    uint32 spellId;
    recvPacket >> spellId;

    SpellEntry const* spellInfo = sSpellTemplate.LookupEntry<SpellEntry>(spellId);
    if (!spellInfo)
        return;

    if (spellInfo->HasAttribute(SPELL_ATTR_CANT_CANCEL))
        return;

    if (IsPassiveSpell(spellInfo))
        return;

    SpellAuraHolder* holder = _player->GetSpellAuraHolder(spellId);

    if (!holder)
        return;

    if (!IsPositiveSpell(spellId, holder->GetCaster(), _player))
    {
        // ignore for remote control state
        if (!_player->IsSelfMover())
        {
            // except own aura spells
            bool allow = false;
            for (unsigned int k : spellInfo->EffectApplyAuraName)
            {
                if (k == SPELL_AURA_MOD_POSSESS ||
                    k == SPELL_AURA_MOD_POSSESS_PET)
                {
                    allow = true;
                    break;
                }
            }

            // this also include case when aura not found
            if (!allow)
                return;
        }
        else
            return;
    }

    // channeled spell case (it currently casted then)
    if (IsChanneledSpell(spellInfo))
    {
        if (Spell* curSpell = _player->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
            if (curSpell->m_spellInfo->Id == spellId)
                _player->InterruptSpell(CURRENT_CHANNELED_SPELL);
        return;
    }

    // not own area auras can't be cancelled (note: maybe need to check for aura on holder and not general on spell)
    if (holder && holder->GetCasterGuid() != _player->GetObjectGuid() && HasAreaAuraEffect(holder->GetSpellProto()))
        return;

    // non channeled case
    _player->RemoveAurasDueToSpellByCancel(spellId);
}
Пример #7
0
void PetAI::UpdateAI(const uint32 diff)
{
    if (!m_unit->isAlive())
        return;
    Creature* creature = (m_unit->GetTypeId() == TYPEID_UNIT) ? static_cast<Creature*>(m_unit) : nullptr;
    Pet* pet = (creature && creature->IsPet()) ? static_cast<Pet*>(m_unit) : nullptr;
    
    Unit* owner = m_unit->GetCharmerOrOwner();
    if (!owner)
        return;

    Unit* victim = (pet && pet->GetModeFlags() & PET_MODE_DISABLE_ACTIONS) ? nullptr : m_unit->getVictim();

    if (m_updateAlliesTimer <= diff)
        // UpdateAllies self set update timer
        UpdateAllies();
    else
        m_updateAlliesTimer -= diff;

    if (inCombat && !victim)
    {
        m_unit->AttackStop(true, true);
        inCombat = false;
    }

    CharmInfo* charminfo = m_unit->GetCharmInfo();
    MANGOS_ASSERT(charminfo);

    if (charminfo->GetIsRetreating())
    {
        if (!owner->IsWithinDistInMap(m_unit, (PET_FOLLOW_DIST * 2)))
        {
            if (!m_unit->hasUnitState(UNIT_STAT_FOLLOW))
                m_unit->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);

            return;
        }
        else
            charminfo->SetIsRetreating();
    }
    else if (charminfo->GetSpellOpener() != 0) // have opener stored
    {
        uint32 minRange = charminfo->GetSpellOpenerMinRange();

        if (!(victim = m_unit->getVictim())
            || (minRange != 0 && m_unit->IsWithinDistInMap(victim, minRange)))
            charminfo->SetSpellOpener();
        else if (m_unit->IsWithinDistInMap(victim, charminfo->GetSpellOpenerMaxRange())
                && m_unit->IsWithinLOSInMap(victim))
        {
            // stop moving
            m_unit->clearUnitState(UNIT_STAT_MOVING);

            // auto turn to target
            m_unit->SetInFront(victim);

            if (victim->GetTypeId() == TYPEID_PLAYER)
                m_unit->SendCreateUpdateToPlayer((Player*)victim);

            if (owner->GetTypeId() == TYPEID_PLAYER)
                m_unit->SendCreateUpdateToPlayer((Player*)owner);

            uint32 spell_id = charminfo->GetSpellOpener();
            SpellEntry const* spellInfo = sSpellTemplate.LookupEntry<SpellEntry>(spell_id);

            Spell* spell = new Spell(m_unit, spellInfo, false);

            SpellCastResult result = spell->CheckPetCast(victim);

            if (result == SPELL_CAST_OK)
            {
                if (creature)
                    creature->AddCreatureSpellCooldown(spell_id);
                spell->SpellStart(&(spell->m_targets));
            }
            else
                delete spell;

            charminfo->SetSpellOpener();
        }
        else
            return;
    }
    // Auto cast (casted only in combat or persistent spells in any state)
    else if (!m_unit->IsNonMeleeSpellCasted(false))
    {
        typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList;
        TargetSpellList targetSpellStore;
        if (pet)
        {
            for (uint8 i = 0; i < pet->GetPetAutoSpellSize(); ++i)
            {
                uint32 spellID = pet->GetPetAutoSpellOnPos(i);
                if (!spellID)
                    continue;

                SpellEntry const* spellInfo = sSpellTemplate.LookupEntry<SpellEntry>(spellID);
                if (!spellInfo)
                    continue;

                if (m_unit->GetCharmInfo() && m_unit->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo))
                    continue;

                // ignore some combinations of combat state and combat/non combat spells
                if (!inCombat)
                {
                    // ignore attacking spells, and allow only self/around spells
                    if (!IsPositiveSpell(spellInfo->Id))
                        continue;

                    // non combat spells allowed
                    // only pet spells have IsNonCombatSpell and not fit this requirements:
                    // Consume Shadows, Lesser Invisibility, so ignore checks for its
                    if (!IsNonCombatSpell(spellInfo))
                    {
                        int32 duration = GetSpellDuration(spellInfo);
                        int32 cooldown = GetSpellRecoveryTime(spellInfo);

                        // allow only spell not on cooldown
                        if (cooldown != 0 && duration < cooldown)
                            continue;

                        // not allow instant kill auto casts as full health cost
                        if (IsSpellHaveEffect(spellInfo, SPELL_EFFECT_INSTAKILL))
                            continue;
                    }
                }
                // just ignore non-combat spells
                else if (IsNonCombatSpell(spellInfo))
                    continue;

                Spell* spell = new Spell(m_unit, spellInfo, false);

                if (inCombat && !m_unit->hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(victim))
                {
                    targetSpellStore.push_back(TargetSpellList::value_type(victim, spell));
                    continue;
                }
                else
                {
                    bool spellUsed = false;
                    for (GuidSet::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar)
                    {
                        Unit* Target = m_unit->GetMap()->GetUnit(*tar);

                        // only buff targets that are in combat, unless the spell can only be cast while out of combat
                        if (!Target)
                            continue;

                        if (spell->CanAutoCast(Target))
                        {
                            targetSpellStore.push_back(TargetSpellList::value_type(Target, spell));
                            spellUsed = true;
                            break;
                        }
                    }
                    if (!spellUsed)
                        delete spell;
                }
            }
        }

        // found units to cast on to
        if (!targetSpellStore.empty())
        {
            uint32 index = urand(0, targetSpellStore.size() - 1);

            Spell* spell  = targetSpellStore[index].second;
            Unit*  target = targetSpellStore[index].first;

            targetSpellStore.erase(targetSpellStore.begin() + index);

            SpellCastTargets targets;
            targets.setUnitTarget(target);

            if (!m_unit->HasInArc(M_PI_F, target))
            {
                m_unit->SetInFront(target);
                if (target->GetTypeId() == TYPEID_PLAYER)
                    m_unit->SendCreateUpdateToPlayer((Player*)target);

                if (owner && owner->GetTypeId() == TYPEID_PLAYER)
                    m_unit->SendCreateUpdateToPlayer((Player*)owner);
            }

            if (creature)
                creature->AddCreatureSpellCooldown(spell->m_spellInfo->Id);

            spell->SpellStart(&targets);
        }

        // deleted cached Spell objects
        for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr)
            delete itr->second;
    }

    // Stop here if casting spell (No melee and no movement)
    if (m_unit->IsNonMeleeSpellCasted(false))
        return;

    // we may get our actions disabled during spell casting, so do entire recheck for victim
    victim = (pet && pet->GetModeFlags() & PET_MODE_DISABLE_ACTIONS) ? nullptr : m_unit->getVictim();

    if (victim)
    {
        // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc.
        // This is needed for charmed creatures, as once their target was reset other effects can trigger threat
        if (!victim->isTargetableForAttack())
        {
            DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "PetAI (guid = %u) is stopping attack.", m_unit->GetGUIDLow());
            m_unit->CombatStop();
            inCombat = false;
            
            return;
        }

        // if pet misses its target, it will also be the first in threat list
        if ((!creature || !(creature->GetCreatureInfo()->ExtraFlags & CREATURE_EXTRA_FLAG_NO_MELEE))
            && m_unit->CanReachWithMeleeAttack(victim))
        {
            if (!m_unit->HasInArc(2 * M_PI_F / 3, victim))
            {
                m_unit->SetInFront(victim);
                if (victim->GetTypeId() == TYPEID_PLAYER)
                    m_unit->SendCreateUpdateToPlayer((Player*)victim);

                if (owner && owner->GetTypeId() == TYPEID_PLAYER)
                    m_unit->SendCreateUpdateToPlayer((Player*)owner);
            }

            DoMeleeAttackIfReady();
        }
        else if (!m_unit->hasUnitState(UNIT_STAT_MOVING))
            AttackStart(victim);
    }
    else if (owner)
    {
        CharmInfo* charmInfo = m_unit->GetCharmInfo();

        if (owner->isInCombat() && !(charmInfo && charmInfo->HasReactState(REACT_PASSIVE)))
            AttackStart(owner->getAttackerForHelper());
        else
        {
            if (charmInfo && charmInfo->HasCommandState(COMMAND_STAY))
            {
                //if stay command is set but we don't have stay pos set then we need to establish current pos as stay position
                if (!charminfo->IsStayPosSet())
                    charminfo->SetStayPosition(true);

                float stayPosX = charminfo->GetStayPosX();
                float stayPosY = charminfo->GetStayPosY();
                float stayPosZ = charminfo->GetStayPosZ();

                if (m_unit->GetPositionX() == stayPosX
                    && m_unit->GetPositionY() == stayPosY
                    && m_unit->GetPositionZ() == stayPosZ)
                {
                    float StayPosO = charminfo->GetStayPosO();

                    if (m_unit->hasUnitState(UNIT_STAT_MOVING))
                    {
                        m_unit->GetMotionMaster()->Clear(false);
                        m_unit->GetMotionMaster()->MoveIdle();
                    }
                    else if (m_unit->GetOrientation() != StayPosO)
                        m_unit->SetOrientation(StayPosO);
                }
                else
                    m_unit->GetMotionMaster()->MovePoint(0, stayPosX, stayPosY, stayPosZ, false);
            }
            else if (m_unit->hasUnitState(UNIT_STAT_FOLLOW))
            {
                if (owner->IsWithinDistInMap(m_unit, PET_FOLLOW_DIST))
                {
                    m_unit->GetMotionMaster()->Clear(false);
                    m_unit->GetMotionMaster()->MoveIdle();
                }
            }
            else if (charmInfo && charmInfo->HasCommandState(COMMAND_FOLLOW)
                && !owner->IsWithinDistInMap(m_unit, (PET_FOLLOW_DIST * 2)))
                m_unit->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
        }
    }
}