예제 #1
0
void UnitAI::MoveInLineOfSight(Unit* who)
{
    if (!HasReactState(REACT_AGGRESSIVE))
        return;

    if (!m_unit->CanFly() && m_unit->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE)
        return;

    if (m_unit->getVictim() && !m_unit->GetMap()->IsDungeon())
        return;

    if (m_unit->IsNeutralToAll())
        return;

    if (who->GetObjectGuid().IsCreature() && who->isInCombat())
        CheckForHelp(who, m_unit, 10.0);

    if (m_unit->CanInitiateAttack() && who->isInAccessablePlaceFor(m_unit))
    {
        if (AssistPlayerInCombat(who))
            return;

        if (m_unit->CanAttackOnSight(who))
            DetectOrAttack(who);
    }
}
예제 #2
0
void ScriptedPetAI::AttackedBy(Unit* attacker)
{
    if (m_creature->getVictim())
        return;

    if (!HasReactState(REACT_PASSIVE) && m_creature->CanReachWithMeleeAttack(attacker))
        AttackStart(attacker);
}
예제 #3
0
void PetAI::AttackedBy(Unit* attacker)
{
    CharmInfo* charminfo = m_unit->GetCharmInfo();
    MANGOS_ASSERT(charminfo);

    // when attacked, fight back if no victim unless we have a charm state set to passive
    if (!(m_unit->getVictim() || charminfo->GetIsRetreating() || HasReactState(REACT_PASSIVE)))
        AttackStart(attacker);
}
예제 #4
0
void UnitAI::AttackStart(Unit* who)
{
    if (!who || HasReactState(REACT_PASSIVE))
        return;

    if (m_unit->Attack(who, m_meleeEnabled))
    {
        m_unit->AddThreat(who);
        m_unit->SetInCombatWith(who);
        who->SetInCombatWith(m_unit);

        HandleMovementOnAttackStart(who);
    }
}
예제 #5
0
void ScriptedPetAI::UpdateAI(const uint32 diff)
{
    if (!m_creature->isAlive())                             // should not be needed, isAlive is checked in mangos before calling UpdateAI
        return;

    // UpdateAllies() is done in the generic PetAI in Mangos, but we can't do this from script side.
    // Unclear what side effects this has, but is something to be resolved from Mangos.

    if (m_creature->getVictim())                            // in combat
    {
        if (!m_creature->CanAttack(m_creature->getVictim()))
        {
            // target no longer valid for pet, so either attack stops or new target are selected
            // doesn't normally reach this, because of how petAi is designed in Mangos. CombatStop
            // are called before this update diff, and then pet will already have no victim.
            ResetPetCombat();
            return;
        }

        // update when in combat
        UpdatePetAI(diff);
    }
    else if (m_creature->GetCharmInfo())
    {
        if (m_creature->isInCombat())
            m_creature->CombatStop(true, true);

        Unit* owner = m_creature->GetMaster();

        if (!owner)
            return;

        if (owner->isInCombat() && !HasReactState(REACT_PASSIVE))
        {
            // Not correct in all cases.
            // When mob initiate attack by spell, pet should not start attack before spell landed.
            AttackStart(owner->getAttackerForHelper());
        }
        else if (m_creature->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))
        {
            // not following, so start follow
            if (!m_creature->hasUnitState(UNIT_STAT_FOLLOW))
                m_creature->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);

            // update when not in combat
            UpdatePetOOCAI(diff);
        }
    }
}
예제 #6
0
void CreatureAI::AttackStart(Unit* who)
{
    if (!who || HasReactState(REACT_PASSIVE))
        return;

    if (m_creature->Attack(who, m_meleeEnabled))
    {
        m_creature->AddThreat(who);
        m_creature->SetInCombatWith(who);
        who->SetInCombatWith(m_unit);

        // Cast "Spawn Guard" to help Civilian
        if (m_creature->IsCivilian())
            m_creature->CastSpell(m_creature, 43783, TRIGGERED_OLD_TRIGGERED);

        HandleMovementOnAttackStart(who);
    }
}
예제 #7
0
void PetAI::MoveInLineOfSight(Unit* who)
{
    if (Unit* victim = m_unit->getVictim())
        if (victim->isAlive())
            return;

    Pet* pet = (m_unit->GetTypeId() == TYPEID_UNIT && static_cast<Creature*>(m_unit)->IsPet()) ? static_cast<Pet*>(m_unit) : nullptr;

    if (HasReactState(REACT_AGGRESSIVE)
            && !(pet && pet->GetModeFlags() & PET_MODE_DISABLE_ACTIONS)
            && m_creature->CanAttackOnSight(who) && who->isInAccessablePlaceFor(m_unit)
            && m_unit->IsWithinDistInMap(who, m_unit->GetAttackDistance(who))
            && m_unit->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE
            && m_unit->IsWithinLOSInMap(who))
    {
        AttackStart(who);

        if (Unit* owner = m_unit->GetOwner())
            owner->SetInCombatState(true, who);
    }
}
예제 #8
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->GetMaster();
    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, m_followDist, m_followAngle);

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

        if (!victim
                || (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 spellId = charminfo->GetSpellOpener();
            SpellEntry const* spellInfo = sSpellTemplate.LookupEntry<SpellEntry>(spellId);

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

            SpellCastResult result = spell->CheckPetCast(victim);

            if (result == SPELL_CAST_OK)
                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->IsSpellReady(*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, TRIGGERED_NONE);

                if (inCombat && spell->CanAutoCast(victim))
                {
                    targetSpellStore.push_back(TargetSpellList::value_type(victim, spell));
                }
                else
                {
                    bool spellUsed = false;
                    for (auto tar : m_AllySet)
                    {
                        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(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);
            }

            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 (!m_unit->CanAttack(victim))
        {
            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 || m_meleeEnabled)
                && m_unit->CanReachWithMeleeAttack(victim))
        {
            if (!m_unit->HasInArc(victim, 2 * M_PI_F / 3))
            {
                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->IsIncapacitated())
    {
        CharmInfo* charmInfo = m_unit->GetCharmInfo();

        const bool staying = (charmInfo && charmInfo->HasCommandState(COMMAND_STAY));
        const bool following = (!staying && charmInfo && charmInfo->HasCommandState(COMMAND_FOLLOW));

        if (owner->isInCombat() && !HasReactState(REACT_PASSIVE) && !staying)
            AttackStart(owner->getAttackerForHelper());
        else
        {
            if (staying)
            {
                //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 (int32(m_unit->GetPositionX()) == int32(stayPosX)
                        && int32(m_unit->GetPositionY()) == int32(stayPosY)
                        && int32(m_unit->GetPositionZ()) == int32(stayPosZ))
                {
                    float StayPosO = charminfo->GetStayPosO();

                    if (m_unit->hasUnitState(UNIT_STAT_MOVING))
                    {
                        m_unit->GetMotionMaster()->Clear(false);
                        m_unit->GetMotionMaster()->MoveIdle();
                    }
                    else if (int32(m_unit->GetOrientation()) != int32(StayPosO))
                        m_unit->SetOrientation(StayPosO);
                }
                else
                    m_unit->GetMotionMaster()->MovePoint(0, stayPosX, stayPosY, stayPosZ, false);
            }
            else if (m_unit->hasUnitState(UNIT_STAT_FOLLOW) && !m_unit->hasUnitState(UNIT_STAT_FOLLOW_MOVE) && owner->IsWithinDistInMap(m_unit, PET_FOLLOW_DIST))
            {
                m_unit->GetMotionMaster()->Clear(false);
                m_unit->GetMotionMaster()->MoveIdle();
            }
            else if (!m_unit->hasUnitState(UNIT_STAT_FOLLOW_MOVE) && !owner->IsWithinDistInMap(m_unit, (PET_FOLLOW_DIST * 2)))
            {
                if (following)
                {
                    m_unit->GetMotionMaster()->Clear(false);
                    m_unit->GetMotionMaster()->MoveFollow(owner, m_followDist, m_followAngle);
                }
            }
        }
    }
}