Esempio n. 1
0
CombatManeuverReturns PlayerbotShamanAI::HealPlayer(Player* target)
{
    CombatManeuverReturns r = PlayerbotClassAI::HealPlayer(target);
    if (r != RETURN_NO_ACTION_OK)
        return r;

    if (!target->isAlive())
    {
        if (ANCESTRAL_SPIRIT && m_ai->CastSpell(ANCESTRAL_SPIRIT, *target))
        {
            std::string msg = "Resurrecting ";
            msg += target->GetName();
            m_bot->Say(msg, LANG_UNIVERSAL);
            return RETURN_CONTINUE;
        }
        return RETURN_NO_ACTION_ERROR; // not error per se - possibly just OOM
    }

    // Remove poison on group members if orders allow bot to do so
    if (Player* pPoisonedTarget = GetDispelTarget(DISPEL_POISON))
    {
        if (CURE_POISON_SHAMAN > 0 && (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_NODISPEL) == 0 && m_ai->CastSpell(CURE_POISON_SHAMAN, *pPoisonedTarget))
            return RETURN_CONTINUE;
    }

    // Remove disease on group members if orders allow bot to do so
    if (Player* pDiseasedTarget = GetDispelTarget(DISPEL_DISEASE))
    {
        if (CURE_DISEASE_SHAMAN > 0 && (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_NODISPEL) == 0 && m_ai->CastSpell(CURE_DISEASE_SHAMAN, *pDiseasedTarget))
            return RETURN_CONTINUE;
    }

    // Everyone is healthy enough, return OK. MUST correlate to highest value below (should be last HP check)
    if (target->GetHealthPercent() >= 80)
        return RETURN_NO_ACTION_OK;

    // Technically the best rotation is CHAIN + LHW + LHW subbing in HW for trouble (bad mana efficiency)
    if (target->GetHealthPercent() < 30 && HEALING_WAVE > 0 && m_ai->CastSpell(HEALING_WAVE, *target))
        return RETURN_CONTINUE;
    if (target->GetHealthPercent() < 50 && LESSER_HEALING_WAVE > 0 && m_ai->CastSpell(LESSER_HEALING_WAVE, *target))
        return RETURN_CONTINUE;
    if (target->GetHealthPercent() < 80 && CHAIN_HEAL > 0 && m_ai->CastSpell(CHAIN_HEAL, *target))
        return RETURN_CONTINUE;

    return RETURN_NO_ACTION_UNKNOWN;
} // end HealTarget
CombatManeuverReturns PlayerbotPaladinAI::DispelPlayer(Player* target)
{
    uint32 dispel = CLEANSE > 0 ? CLEANSE : PURIFY;
    // Remove negative magic on group members
    if (Player* cursedTarget = GetDispelTarget(DISPEL_MAGIC))
    {
        CombatManeuverReturns r = PlayerbotClassAI::DispelPlayer(cursedTarget);
        if (r != RETURN_NO_ACTION_OK)
            return r;

        if (dispel > 0 && m_ai->CastSpell(dispel, *cursedTarget) == SPELL_CAST_OK)
            return RETURN_CONTINUE;
    }

    // Remove poison on group members
    if (Player* poisonedTarget = GetDispelTarget(DISPEL_POISON))
    {
        CombatManeuverReturns r = PlayerbotClassAI::DispelPlayer(poisonedTarget);
        if (r != RETURN_NO_ACTION_OK)
            return r;

        if (dispel > 0 && m_ai->CastSpell(dispel, *poisonedTarget) == SPELL_CAST_OK)
            return RETURN_CONTINUE;
    }

    // Remove disease on group members
    if (Player* diseasedTarget = GetDispelTarget(DISPEL_DISEASE))
    {
        CombatManeuverReturns r = PlayerbotClassAI::DispelPlayer(diseasedTarget);
        if (r != RETURN_NO_ACTION_OK)
            return r;

        if (dispel > 0 && m_ai->CastSpell(dispel, *diseasedTarget) == SPELL_CAST_OK)
            return RETURN_CONTINUE;
    }
    return RETURN_NO_ACTION_OK;
}
Esempio n. 3
0
CombatManeuverReturns PlayerbotPriestAI::HealPlayer(Player* target)
{
    CombatManeuverReturns r = PlayerbotClassAI::HealPlayer(target);
    if (r != RETURN_NO_ACTION_OK)
        return r;

    if (!target->isAlive())
    {
        if (RESURRECTION && m_ai->In_Reach(target,RESURRECTION) && m_ai->CastSpell(RESURRECTION, *target))
        {
            std::string msg = "Resurrecting ";
            msg += target->GetName();
            m_bot->Say(msg, LANG_UNIVERSAL);
            return RETURN_CONTINUE;
        }
        return RETURN_NO_ACTION_ERROR; // not error per se - possibly just OOM
    }

    // Remove negative magic on group members if orders allow bot to do so
    if (Player* pCursedTarget = GetDispelTarget(DISPEL_MAGIC))
    {
        if (PRIEST_DISPEL_MAGIC > 0 && (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_NODISPEL) == 0 && CastSpell(PRIEST_DISPEL_MAGIC, pCursedTarget))
            return RETURN_CONTINUE;
    }

    // Remove disease on group members if orders allow bot to do so
    if (Player* pDiseasedTarget = GetDispelTarget(DISPEL_DISEASE))
    {
        uint32 cure = ABOLISH_DISEASE > 0 ? ABOLISH_DISEASE : CURE_DISEASE;
        // uint32 poison = ABOLISH_POISON ? ABOLISH_POISON : CURE_POISON;
        if (cure > 0 && (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_NODISPEL) == 0 && CastSpell(cure, pDiseasedTarget))
            return RETURN_CONTINUE;
    }

    uint8 hp = target->GetHealthPercent();
    uint8 hpSelf = m_ai->GetHealthPercent();

    // Define a tank bot will look at
    Unit* pMainTank = GetHealTarget(JOB_TANK);

    if (hp >= 90)
        return RETURN_NO_ACTION_OK;

    // If target is out of range (40 yards) and is a tank: move towards it
    // Other classes have to adjust their position to the healers
    // TODO: This code should be common to all healers and will probably
    // move to a more suitable place
    if (pMainTank && !m_ai->In_Reach(pMainTank, FLASH_HEAL))
    {
        m_bot->GetMotionMaster()->MoveFollow(target, 39.0f, m_bot->GetOrientation());
        return RETURN_CONTINUE;
    }

    // Get a free and more efficient heal if needed: low mana for bot or average health for target
    if (m_ai->IsInCombat() && (hp < 50 || m_ai->GetManaPercent() < 40))
        if (INNER_FOCUS > 0 && m_bot->IsSpellReady(INNER_FOCUS) && !m_bot->HasAura(INNER_FOCUS, EFFECT_INDEX_0) && CastSpell(INNER_FOCUS, m_bot))
            return RETURN_CONTINUE;

    if (hp < 25 && POWER_WORD_SHIELD > 0 && m_ai->In_Reach(target,POWER_WORD_SHIELD) && !m_bot->HasAura(POWER_WORD_SHIELD, EFFECT_INDEX_0) && !target->HasAura(WEAKNED_SOUL,EFFECT_INDEX_0) && m_ai->CastSpell(POWER_WORD_SHIELD, *target))
        return RETURN_CONTINUE;
    if (hp < 35 && FLASH_HEAL > 0 && m_ai->In_Reach(target,FLASH_HEAL) && m_ai->CastSpell(FLASH_HEAL, *target))
        return RETURN_CONTINUE;
    if (hp < 50 && GREATER_HEAL > 0 && m_ai->In_Reach(target,GREATER_HEAL) && m_ai->CastSpell(GREATER_HEAL, *target))
        return RETURN_CONTINUE;
    // Heals target AND self for equal amount
    if (hp < 60 && hpSelf < 80 && BINDING_HEAL > 0 && m_ai->In_Reach(target,BINDING_HEAL) && m_ai->CastSpell(BINDING_HEAL, *target))
        return RETURN_CONTINUE;
    if (hp < 60 && PRAYER_OF_MENDING > 0 && m_ai->In_Reach(target,PRAYER_OF_MENDING) && !target->HasAura(PRAYER_OF_MENDING, EFFECT_INDEX_0) && CastSpell(PRAYER_OF_MENDING, target))
        return RETURN_FINISHED_FIRST_MOVES;
    if (hp < 70 && HEAL > 0 && m_ai->In_Reach(target,HEAL) && m_ai->CastSpell(HEAL, *target))
        return RETURN_CONTINUE;
    if (hp < 90 && RENEW > 0 && m_ai->In_Reach(target,RENEW) && !target->HasAura(RENEW) && m_ai->CastSpell(RENEW, *target))
        return RETURN_CONTINUE;

    // Group heal. Not really useful until a group check is available?
    //if (hp < 40 && PRAYER_OF_HEALING > 0 && m_ai->CastSpell(PRAYER_OF_HEALING, *target) & RETURN_CONTINUE)
    //    return RETURN_CONTINUE;
    // Group heal. Not really useful until a group check is available?
    //if (hp < 50 && CIRCLE_OF_HEALING > 0 && m_ai->CastSpell(CIRCLE_OF_HEALING, *target) & RETURN_CONTINUE)
    //    return RETURN_CONTINUE;

    return RETURN_NO_ACTION_OK;
} // end HealTarget
Esempio n. 4
0
void PlayerbotMageAI::DoNonCombatActions()
{
    Player* master = GetMaster();

    if (!m_bot || !master)
        return;

    // Remove curse on group members if orders allow bot to do so
    if (Player* pCursedTarget = GetDispelTarget(DISPEL_CURSE))
    {
        if (MAGE_REMOVE_CURSE > 0 && (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_NODISPEL) == 0 && CastSpell(MAGE_REMOVE_CURSE, pCursedTarget))
            return;
    }

    // Buff armor
    if (MAGE_ARMOR)
    {
        if (m_ai->SelfBuff(MAGE_ARMOR))
            return;
    }
    else if (ICE_ARMOR)
    {
        if (m_ai->SelfBuff(ICE_ARMOR))
            return;
    }
    else if (FROST_ARMOR)
    {
        if (m_ai->SelfBuff(FROST_ARMOR))
            return;
    }

    if (COMBUSTION && !m_bot->HasSpellCooldown(COMBUSTION) && m_ai->SelfBuff(COMBUSTION))
        return;

    // buff group
    // the check for group targets is performed by NeedGroupBuff (if group is found for bots by the function)
    if (NeedGroupBuff(ARCANE_BRILLIANCE, ARCANE_INTELLECT) && m_ai->HasSpellReagents(ARCANE_BRILLIANCE))
    {
        if (Buff(&PlayerbotMageAI::BuffHelper, ARCANE_BRILLIANCE) & RETURN_CONTINUE)
            return;
    }
    else if (Buff(&PlayerbotMageAI::BuffHelper, ARCANE_INTELLECT, JOB_MANAONLY) & RETURN_CONTINUE)
        return;

    Item* gem = FindManaGem();
    if (!gem && CONJURE_MANA_GEM && m_ai->CastSpell(CONJURE_MANA_GEM, *m_bot))
    {
        m_ai->SetIgnoreUpdateTime(3);
        return;
    }

    // TODO: The beauty of a mage is not only its ability to supply itself with water, but to share its water
    // So, conjure at *least* 1.25 stacks, ready to trade a stack and still have some left for self
    if (m_ai->FindDrink() == nullptr && CONJURE_WATER && m_ai->CastSpell(CONJURE_WATER, *m_bot))
    {
        m_ai->TellMaster("I'm conjuring some water.");
        m_ai->SetIgnoreUpdateTime(3);
        return;
    }
    if (m_ai->FindFood() == nullptr && CONJURE_FOOD && m_ai->CastSpell(CONJURE_FOOD, *m_bot))
    {
        m_ai->TellMaster("I'm conjuring some food.");
        m_ai->SetIgnoreUpdateTime(3);
        return;
    }

    if (EatDrinkBandage())
        return;
} // end DoNonCombatActions
Esempio n. 5
0
CombatManeuverReturns PlayerbotMageAI::DoNextCombatManeuverPVE(Unit *pTarget)
{
    if (!m_ai)  return RETURN_NO_ACTION_ERROR;
    if (!m_bot) return RETURN_NO_ACTION_ERROR;

    Unit* pVictim = pTarget->getVictim();
    bool meleeReach = m_bot->CanReachWithMeleeAttack(pTarget);

    uint32 spec = m_bot->GetSpec();

    if (m_ai->GetCombatStyle() != PlayerbotAI::COMBAT_RANGED && !meleeReach)
        m_ai->SetCombatStyle(PlayerbotAI::COMBAT_RANGED);
    // switch to melee if in melee range AND can't shoot OR have no ranged (wand) equipped
    else if(m_ai->GetCombatStyle() != PlayerbotAI::COMBAT_MELEE
            && meleeReach
            && (SHOOT == 0 || !m_bot->GetWeaponForAttack(RANGED_ATTACK, true, true)))
        m_ai->SetCombatStyle(PlayerbotAI::COMBAT_MELEE);

    //Used to determine if this bot is highest on threat
    Unit *newTarget = m_ai->FindAttacker((PlayerbotAI::ATTACKERINFOTYPE) (PlayerbotAI::AIT_VICTIMSELF | PlayerbotAI::AIT_HIGHESTTHREAT), m_bot);

    // Remove curse on group members
    if (Player* pCursedTarget = GetDispelTarget(DISPEL_CURSE))
    {
        if (MAGE_REMOVE_CURSE > 0 && CastSpell(MAGE_REMOVE_CURSE, pCursedTarget))
            return RETURN_CONTINUE;
    }

    if (newTarget && !m_ai->IsNeutralized(newTarget)) // Bot has aggro and the mob is not already crowd controled
    {
        if (newTarget->GetHealthPercent() > 25)
        {
            // If elite
            if (m_ai->IsElite(newTarget))
            {
                // If the attacker is a beast or humanoid, let's the bot give it a form more suited to the low intellect of something fool enough to attack a mage
                Creature * pCreature = (Creature*) newTarget;
                if (pCreature && (pCreature->GetCreatureInfo()->CreatureType == CREATURE_TYPE_HUMANOID || pCreature->GetCreatureInfo()->CreatureType == CREATURE_TYPE_BEAST))
                {
                    if (POLYMORPH > 0 && CastSpell(POLYMORPH, newTarget))
                        return RETURN_CONTINUE;
                }

                // Things are getting dire: cast Ice block
                if (ICE_BLOCK > 0 && !m_bot->HasSpellCooldown(ICE_BLOCK) && m_ai->GetHealthPercent() < 30 && !m_bot->HasAura(ICE_BLOCK, EFFECT_INDEX_0) && m_ai->CastSpell(ICE_BLOCK))
                    return RETURN_CONTINUE;

                // Cast Ice Barrier if health starts to goes low
                if (ICE_BARRIER > 0 && !m_bot->HasSpellCooldown(ICE_BARRIER) && m_ai->GetHealthPercent() < 50 && !m_bot->HasAura(ICE_BARRIER) && m_ai->SelfBuff(ICE_BARRIER))
                    return RETURN_CONTINUE;

                // Have threat, can't quickly lower it. 3 options remain: Stop attacking, lowlevel damage (wand), keep on keeping on.
                return CastSpell(SHOOT, pTarget);
            }
            else // not elite
            {
                // Cast mana shield if no shield is already up
                if (MANA_SHIELD > 0 && m_ai->GetHealthPercent() < 70 && !m_bot->HasAura(MANA_SHIELD) && !m_bot->HasAura(ICE_BARRIER) && m_ai->SelfBuff(MANA_SHIELD))
                    return RETURN_CONTINUE;
            }
        }
    }

    // Mana check and replenishment
    if (EVOCATION && m_ai->GetManaPercent() <= 10 && !m_bot->HasSpellCooldown(EVOCATION) && !newTarget && m_ai->SelfBuff(EVOCATION))
        return RETURN_CONTINUE;
    if (m_ai->GetManaPercent() <= 20)
    {
        Item* gem = FindManaGem();
        if (gem)
            m_ai->UseItem(gem);
    }

    // If bot has frost/fire resist order use Frost/Fire Ward when available
    if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_RESIST_FROST && FROST_WARD && !m_bot->HasSpellCooldown(FROST_WARD) && m_ai->SelfBuff(FROST_WARD))
        return RETURN_CONTINUE;
    if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_RESIST_FIRE && FIRE_WARD && !m_bot->HasSpellCooldown(FIRE_WARD) && m_ai->SelfBuff(FIRE_WARD))
        return RETURN_CONTINUE;

    if (COUNTERSPELL > 0 && !m_bot->HasSpellCooldown(COUNTERSPELL) && pTarget->IsNonMeleeSpellCasted(true) && CastSpell(COUNTERSPELL, pTarget))
        return RETURN_CONTINUE;

    // If Clearcasting is active, cast arcane missiles
    // Bot could also cast flamestrike or blizzard for free, but the AoE could break some crowd control
    // or add threat on mobs ignoring the bot currently, so only focus on the bot's current target
    if (m_bot->HasAura(CLEARCASTING_1) && ARCANE_MISSILES > 0 && CastSpell(ARCANE_MISSILES, pTarget))
    {
        m_ai->SetIgnoreUpdateTime(3);
        return RETURN_CONTINUE;
    }

    switch (spec)
    {
    case MAGE_SPEC_FROST:
        if (COLD_SNAP && !m_bot->HasSpellCooldown(COLD_SNAP) && CheckFrostCooldowns() > 2 && m_ai->SelfBuff(COLD_SNAP))  // Clear frost spell cooldowns if bot has more than 2 active
            return RETURN_CONTINUE;
        if (CONE_OF_COLD > 0 && !m_bot->HasSpellCooldown(CONE_OF_COLD) && meleeReach)
        {
            // Cone of Cold does not require a target, so ensure that the bot faces the current one before casting
            m_ai->FaceTarget(pTarget);
            if (m_ai->CastSpell(CONE_OF_COLD))
                return RETURN_CONTINUE;
        }
        if (FROSTBOLT > 0 && m_ai->In_Reach(pTarget,FROSTBOLT) && !pTarget->HasAura(FROSTBOLT, EFFECT_INDEX_0) && CastSpell(FROSTBOLT, pTarget))
            return RETURN_CONTINUE;
        if (FROST_NOVA > 0 && !m_bot->HasSpellCooldown(FROST_NOVA) && meleeReach && !pTarget->HasAura(FROST_NOVA, EFFECT_INDEX_0) && CastSpell(FROST_NOVA, pTarget))
            return RETURN_CONTINUE;
        // Default frost spec action
        if (FROSTBOLT > 0 && m_ai->In_Reach(pTarget,FROSTBOLT))
            return CastSpell(FROSTBOLT, pTarget);
        /*
        if (BLIZZARD > 0 && m_ai->In_Reach(pTarget,BLIZZARD) && m_ai->GetAttackerCount() >= 5 && CastSpell(BLIZZARD, pTarget))
        {
            m_ai->SetIgnoreUpdateTime(8);
            return RETURN_CONTINUE;
        }
        */
        break;

    case MAGE_SPEC_FIRE:
        if (COMBUSTION > 0 && m_ai->SelfBuff(COMBUSTION))
            return RETURN_CONTINUE;
        if (BLAST_WAVE > 0 && m_ai->GetAttackerCount() >= 3 && meleeReach && CastSpell(BLAST_WAVE, pTarget))
            return RETURN_CONTINUE;
        // Try to have 3 scorch stacks to let tank build aggro while getting a nice crit% bonus
        if (IMPROVED_SCORCH > 0 && SCORCH > 0)
        {
            if (!pTarget->HasAura(FIRE_VULNERABILITY, EFFECT_INDEX_0) && CastSpell(SCORCH, pTarget))   // no stacks: cast it
                return RETURN_CONTINUE;
            else
            {
                SpellAuraHolder* holder = pTarget->GetSpellAuraHolder(FIRE_VULNERABILITY);
                if (holder && (holder->GetStackAmount() < 3) && CastSpell(SCORCH, pTarget))
                    return RETURN_CONTINUE;
            }
        }
        // At least 3 stacks of Scorch: cast an opening fireball
        if (FIREBALL > 0 && !pTarget->HasAura(FIREBALL, EFFECT_INDEX_1) && CastSpell(FIREBALL, pTarget))
            return RETURN_CONTINUE;
        // 3 stacks of Scorch and fireball DoT: use fire blast if available
        if (FIRE_BLAST > 0 && !m_bot->HasSpellCooldown(FIRE_BLAST) && CastSpell(FIRE_BLAST, pTarget))
            return RETURN_CONTINUE;
        // All DoTs, cooldowns used, try to maximise scorch stacks (5) to get a even nicer crit% bonus
        if (IMPROVED_SCORCH > 0 && SCORCH > 0)
        {
            SpellAuraHolder* holder = pTarget->GetSpellAuraHolder(FIRE_VULNERABILITY);
            if (holder && (holder->GetStackAmount() < 5) && CastSpell(SCORCH, pTarget))
                return RETURN_CONTINUE;
        }
        // Default fire spec action
        if (FIREBALL > 0 && m_ai->In_Reach(pTarget,FIREBALL))
            return CastSpell(FIREBALL, pTarget);
        /*
        if (FLAMESTRIKE > 0 && m_ai->In_Reach(pTarget,FLAMESTRIKE) && CastSpell(FLAMESTRIKE, pTarget))
            return RETURN_CONTINUE;
        */
        break;

    case MAGE_SPEC_ARCANE:
        if (ARCANE_POWER > 0 && !m_bot->HasSpellCooldown(ARCANE_POWER) && m_ai->IsElite(pTarget) && m_ai->CastSpell(ARCANE_POWER))    // Do not waste Arcane Power on normal NPCs as the bot is likely in a group
            return RETURN_CONTINUE;
        if (PRESENCE_OF_MIND > 0 && !m_bot->HasAura(PRESENCE_OF_MIND) && !m_bot->HasSpellCooldown(PRESENCE_OF_MIND) && m_ai->IsElite(pTarget) && m_ai->SelfBuff(PRESENCE_OF_MIND))
            return RETURN_CONTINUE;
        // If bot has presence of mind active, cast long casting time spells
        if (PRESENCE_OF_MIND && m_bot->HasAura(PRESENCE_OF_MIND))
        {
            // Instant Pyroblast, yeah! Tanks will probably hate this, but what do they know about power? Nothing...
            if (PYROBLAST > 0 && CastSpell(PYROBLAST, pTarget))
                return RETURN_CONTINUE;
            if (FIREBALL > 0 && CastSpell(FIREBALL, pTarget))
                return RETURN_CONTINUE;
        }
        if (ARCANE_EXPLOSION > 0 && m_ai->GetAttackerCount() >= 3 && meleeReach && CastSpell(ARCANE_EXPLOSION, pTarget))
            return RETURN_CONTINUE;
        // Default arcane spec actions (yes, two fire spells)
        if (FIRE_BLAST > 0 && !m_bot->HasSpellCooldown(FIRE_BLAST) && CastSpell(FIRE_BLAST, pTarget))
            return RETURN_CONTINUE;
        if (FIREBALL > 0 && m_ai->In_Reach(pTarget,FIREBALL))
            return CastSpell(FIREBALL, pTarget);
        // If no fireball, arcane missiles
        if (ARCANE_MISSILES > 0 && CastSpell(ARCANE_MISSILES, pTarget))
        {
            m_ai->SetIgnoreUpdateTime(3);
            return RETURN_CONTINUE;
        }
        break;
    }

    // No spec due to low level OR no spell found yet
    if (FROSTBOLT > 0 && m_ai->In_Reach(pTarget,FROSTBOLT) && !pTarget->HasAura(FROSTBOLT, EFFECT_INDEX_0) && CastSpell(FROSTBOLT, pTarget))
        return RETURN_CONTINUE;
    if (FIREBALL > 0 && m_ai->In_Reach(pTarget,FIREBALL) && CastSpell(FIREBALL, pTarget)) // Very low levels
        return RETURN_CONTINUE;

    // Default: shoot with wand
    return CastSpell(SHOOT, pTarget);

    return RETURN_NO_ACTION_ERROR; // What? Not even Fireball or wand are available?
} // end DoNextCombatManeuver
CombatManeuverReturns PlayerbotPaladinAI::HealPlayer(Player* target)
{
    CombatManeuverReturns r = PlayerbotClassAI::HealPlayer(target);
    if (r != RETURN_NO_ACTION_OK)
        return r;

    if (!target->isAlive())
    {
        if (REDEMPTION && m_ai->CastSpell(REDEMPTION, *target))
        {
            std::string msg = "Resurrecting ";
            msg += target->GetName();
            m_bot->Say(msg, LANG_UNIVERSAL);
            return RETURN_CONTINUE;
        }
        return RETURN_NO_ACTION_ERROR; // not error per se - possibly just OOM
    }

    uint32 dispel = CLEANSE > 0 ? CLEANSE : PURIFY;
    // Remove negative magic on group members if orders allow bot to do so
    if (Player* pCursedTarget = GetDispelTarget(DISPEL_MAGIC))
    {
        if (dispel > 0 && (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_NODISPEL) == 0 && m_ai->CastSpell(dispel, *pCursedTarget))
            return RETURN_CONTINUE;
    }
    // Remove poison on group members if orders allow bot to do so
    if (Player* pPoisonedTarget = GetDispelTarget(DISPEL_POISON))
    {
        m_ai->TellMaster("Has poison %s :", pPoisonedTarget->GetName());
        if (dispel > 0 && (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_NODISPEL) == 0 && m_ai->CastSpell(dispel, *pPoisonedTarget))
            return RETURN_CONTINUE;
    }

    // Remove disease on group members if orders allow bot to do so
    if (Player* pDiseasedTarget = GetDispelTarget(DISPEL_DISEASE))
    {
        if (dispel > 0 && (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_NODISPEL) == 0 && m_ai->CastSpell(dispel, *pDiseasedTarget))
            return RETURN_CONTINUE;
    }

    // Define a tank bot will look at
    Unit* pMainTank = GetHealTarget(JOB_TANK);

    // If target is out of range (40 yards) and is a tank: move towards it
    // Other classes have to adjust their position to the healers
    // TODO: This code should be common to all healers and will probably
    // move to a more suitable place
    if (pMainTank && !m_ai->In_Reach(pMainTank, FLASH_OF_LIGHT))
    {
        m_bot->GetMotionMaster()->MoveFollow(target, 39.0f, m_bot->GetOrientation());
        return RETURN_CONTINUE;
    }

    uint8 hp = target->GetHealthPercent();

    // Everyone is healthy enough, return OK. MUST correlate to highest value below (should be last HP check)
    if (hp >= 90)
        return RETURN_NO_ACTION_OK;

    if (hp < 10 && LAY_ON_HANDS && m_bot->IsSpellReady(LAY_ON_HANDS) && m_ai->In_Reach(target, LAY_ON_HANDS) && m_ai->CastSpell(LAY_ON_HANDS, *target))
        return RETURN_CONTINUE;

    // Target is a moderately wounded healer or a badly wounded not tank? Blessing of Protection!
    if (BLESSING_OF_PROTECTION > 0
            && ((hp < 25 && (GetTargetJob(target) & JOB_HEAL)) || (hp < 15 && !(GetTargetJob(target) & JOB_TANK)))
            && m_bot->IsSpellReady(BLESSING_OF_PROTECTION) && m_ai->In_Reach(target, BLESSING_OF_PROTECTION)
            && !target->HasAura(FORBEARANCE, EFFECT_INDEX_0)
            && !target->HasAura(BLESSING_OF_PROTECTION, EFFECT_INDEX_0) && !target->HasAura(DIVINE_PROTECTION, EFFECT_INDEX_0)
            && !target->HasAura(DIVINE_SHIELD, EFFECT_INDEX_0)
            && m_ai->CastSpell(BLESSING_OF_PROTECTION, *target))
        return RETURN_CONTINUE;

    // Low HP : activate Divine Favor to make next heal a critical heal
    if (hp < 25 && DIVINE_FAVOR > 0 && !m_bot->HasAura(DIVINE_FAVOR, EFFECT_INDEX_0) && m_bot->IsSpellReady(DIVINE_FAVOR) && m_ai->CastSpell(DIVINE_FAVOR, *m_bot))
        return RETURN_CONTINUE;

    if (hp < 40 && FLASH_OF_LIGHT && m_ai->In_Reach(target, FLASH_OF_LIGHT) && m_ai->CastSpell(FLASH_OF_LIGHT, *target))
        return RETURN_CONTINUE;

    if (hp < 60 && HOLY_SHOCK && m_ai->In_Reach(target, HOLY_SHOCK) && m_ai->CastSpell(HOLY_SHOCK, *target))
        return RETURN_CONTINUE;

    if (hp < 90 && HOLY_LIGHT && m_ai->In_Reach(target, HOLY_LIGHT) && m_ai->CastSpell(HOLY_LIGHT, *target))
        return RETURN_CONTINUE;

    return RETURN_NO_ACTION_UNKNOWN;
} // end HealTarget
CombatManeuverReturns PlayerbotDruidAI::HealPlayer(Player* target)
{
    CombatManeuverReturns r = PlayerbotClassAI::HealPlayer(target);
    if (r != RETURN_NO_ACTION_OK)
        return r;

    if (!target->isAlive())
    {
        if (m_bot->isInCombat())
        {
            if (REBIRTH && m_ai->In_Reach(target, REBIRTH) && m_bot->IsSpellReady(REBIRTH) && m_ai->CastSpell(REBIRTH, *target))
            {
                std::string msg = "Resurrecting ";
                msg += target->GetName();
                m_bot->Say(msg, LANG_UNIVERSAL);
                return RETURN_CONTINUE;
            }
        }

        return RETURN_NO_ACTION_ERROR; // not error per se - possibly just OOM
    }

    // Remove curse on group members if orders allow bot to do so
    if (Player* pCursedTarget = GetDispelTarget(DISPEL_CURSE))
    {
        if (REMOVE_CURSE > 0 && (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_NODISPEL) == 0 && CastSpell(REMOVE_CURSE, pCursedTarget))
            return RETURN_CONTINUE;
    }

    // Remove poison on group members if orders allow bot to do so
    if (Player* pPoisonedTarget = GetDispelTarget(DISPEL_POISON))
    {
        uint32 cure = ABOLISH_POISON > 0 ? ABOLISH_POISON : CURE_POISON;
        if (cure > 0 && (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_NODISPEL) == 0 && CastSpell(cure, pPoisonedTarget))
            return RETURN_CONTINUE;
    }

    uint8 hp = target->GetHealthPercent();

    // Define a tank bot will look at
    Unit* pMainTank = GetHealTarget(JOB_TANK);

    // If target is out of range (40 yards) and is a tank: move towards it
    // Other classes have to adjust their position to the healers
    // TODO: This code should be common to all healers and will probably
    // move to a more suitable place
    if (pMainTank && !m_ai->In_Reach(pMainTank, HEALING_TOUCH))
    {
        m_bot->GetMotionMaster()->MoveFollow(target, 39.0f, m_bot->GetOrientation());
        return RETURN_CONTINUE;
    }

    // Everyone is healthy enough, return OK. MUST correlate to highest value below (should be last HP check)
    if (hp >= 80)
        return RETURN_NO_ACTION_OK;

    // Start heals. Do lowest HP checks at the top

    // Emergency heal: target needs to be healed NOW!
    if ((target == pMainTank && hp < 10) || (target != pMainTank && hp < 15))
    {
        // first try Nature's Swiftness + Healing Touch: instant heal
        if (NATURES_SWIFTNESS > 0 && m_bot->IsSpellReady(NATURES_SWIFTNESS) && CastSpell(NATURES_SWIFTNESS, m_bot))
            return RETURN_CONTINUE;

        if (HEALING_TOUCH > 0 && m_bot->HasAura(NATURES_SWIFTNESS, EFFECT_INDEX_0) && m_ai->In_Reach(target, HEALING_TOUCH) && CastSpell(HEALING_TOUCH, target))
            return RETURN_CONTINUE;

        // Else try to Swiftmend the target if druid HoT is active on it
        if (SWIFTMEND > 0 && m_bot->IsSpellReady(SWIFTMEND) && m_ai->In_Reach(target, SWIFTMEND) && (target->HasAura(REJUVENATION) || target->HasAura(REGROWTH)) && CastSpell(SWIFTMEND, target))
            return RETURN_CONTINUE;
    }

    // Urgent heal: target won't die next second, but first bot needs to gain some time to cast Healing Touch safely
    if ((target == pMainTank && hp < 15) || (target != pMainTank && hp < 25))
    {
        if (REGROWTH > 0 && m_ai->In_Reach(target, REGROWTH) && !target->HasAura(REGROWTH) && CastSpell(REGROWTH, target))
            return RETURN_CONTINUE;
        if (REJUVENATION > 0 && m_ai->In_Reach(target, REJUVENATION) && target->HasAura(REGROWTH) && !target->HasAura(REJUVENATION) && CastSpell(REJUVENATION, target))
            return RETURN_CONTINUE;
        if (SWIFTMEND > 0 && m_bot->IsSpellReady(SWIFTMEND) && m_ai->In_Reach(target, SWIFTMEND) && (target->HasAura(REJUVENATION) || target->HasAura(REGROWTH)) && CastSpell(SWIFTMEND, target))
            return RETURN_CONTINUE;
    }

    if (hp < 60 && HEALING_TOUCH > 0 && m_ai->In_Reach(target, HEALING_TOUCH) && CastSpell(HEALING_TOUCH, target))
        return RETURN_CONTINUE;

    if (hp < 80 && REJUVENATION > 0 && m_ai->In_Reach(target, REJUVENATION) && !target->HasAura(REJUVENATION) && CastSpell(REJUVENATION, target))
        return RETURN_CONTINUE;

    return RETURN_NO_ACTION_UNKNOWN;
} // end HealTarget