Example #1
0
        /// <summary>
        /// stays within range of Tank as they move.  settings configurable by user.
        /// </summary>
        /// <param name="gapCloser">ability to close distance more quickly than running (such as Roll)</param>
        /// <returns></returns>
        public static Composite CreateStayNearTankBehavior(Composite gapCloser = null)
        {
            int moveNearTank;
            int stopNearTank;

            if (gapCloser == null)
                gapCloser = new ActionAlwaysFail();

            if (!SingularSettings.Instance.StayNearTank)
                return new ActionAlwaysFail();

            if (SingularRoutine.CurrentWoWContext != WoWContext.Instances)
                return new ActionAlwaysFail();

            if (IsThisBehaviorCalledDuringCombat())
            {
                moveNearTank = Math.Max(5, SingularSettings.Instance.StayNearTankRangeCombat);
                stopNearTank = (moveNearTank * 7) / 10;
            }
            else
            {
                moveNearTank = Math.Max(5, SingularSettings.Instance.StayNearTankRangeRest);
                stopNearTank = (moveNearTank * 6) / 10;     // be slightly more elastic at rest
            }

            Logger.WriteDebug("StayNearTank in {0}: will move towards at {1} yds and stop if within {2} yds", Dynamics.CompositeBuilder.CurrentBehaviorType, moveNearTank, stopNearTank);
            return new PrioritySelector(
                ctx => HealerManager.TankToStayNear,

                // no healing needed, then move within heal range of tank
                new ThrottlePasses(
                    1,
                    TimeSpan.FromSeconds(5),
                    RunStatus.Failure,
                        new Action( t => {
                            if (SingularSettings.Debug)
                            {
                                WoWUnit tankToStayNear = (WoWUnit)t;
                                if (t != null)
                                {
                                    ;
                                }
                                else if (!Group.Tanks.Any())
                                    Logger.WriteDiagnostic(Color.HotPink, "TankToStayNear: no group members with Role=Tank");
                                else
                                {
                                    Logger.WriteDebug(Color.HotPink, "TankToStayNear:  {0} tanks in group", Group.Tanks.Count());
                                    int i = 0;
                                    foreach (var tank in Group.Tanks.OrderByDescending(gt => gt == RaFHelper.Leader).ThenBy(gt => gt.DistanceSqr))
                                    {
                                        Logger.WriteDebug(Color.HotPink, "TankToStayNear[{0}]: {1} Health={2:F1}%, Dist={3:F1}, TankPt={4}, MePt={5}, TankMov={6}, MeMov={7}, LoS={8}, LoSS={9}, Combat={10}, MeCombat={11}, ",
                                            i++,
                                            tank.SafeName(),
                                            tank.HealthPercent,
                                            tank.SpellDistance(),
                                            tank.Location,
                                            Me.Location,
                                            tank.IsMoving.ToYN(),
                                            Me.IsMoving.ToYN(),
                                            tank.InLineOfSight.ToYN(),
                                            tank.InLineOfSpellSight.ToYN(),
                                            tank.Combat.ToYN(),
                                            Me.Combat.ToYN()
                                            );
                                    }

                                    Logger.WriteDebug(Color.HotPink, "TankToStayNear: current TargetList has {0} units", Targeting.Instance.TargetList.Count());
                                    i = 0;
                                    foreach (var target in Targeting.Instance.TargetList)
                                    {
                                        Logger.WriteDebug(Color.HotPink, "CurrentTargets[{0}]: {1} {2:F1}% @ {3:F1}",
                                            i++,
                                            target.SafeName(),
                                            target.HealthPercent,
                                            target.SpellDistance()
                                            );
                                    }
                                }
                            }
                            return RunStatus.Failure;
                        })
                    ),
                new Decorator(
                    ret => ((WoWUnit)ret) != null,
                    new Sequence(
                        new PrioritySelector(
                            gapCloser,
                            Movement.CreateMoveToLosBehavior(unit => ((WoWUnit)unit)),
                            Movement.CreateMoveToUnitBehavior(unit => ((WoWUnit)unit), moveNearTank, stopNearTank)
                            // , Movement.CreateEnsureMovementStoppedBehavior(stopNearTank, unit => (WoWUnit)unit, "in heal range of tank")
                            ),
                        new ActionAlwaysFail()
                        )
                    )
                );
        }
Example #2
0
        public static Composite CreateHunterCombatBuffs()
        {
            Composite feignDeathBehavior = new ActionAlwaysFail();

            if ( SingularRoutine.CurrentWoWContext == WoWContext.Normal )
                feignDeathBehavior = CreateFeignDeath( req => NeedFeignDeath, () => TimeSpan.FromSeconds(8), cancel => !Unit.NearbyUnfriendlyUnits.Any(u => u.Distance < 25));

            if ( SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds )
                feignDeathBehavior = CreateFeignDeath(req => NeedFeignDeath, () => TimeSpan.FromSeconds((new Random()).Next(3)), ret => false);

            if ( SingularRoutine.CurrentWoWContext == WoWContext.Instances && HunterSettings.FeignDeathInInstances )
                feignDeathBehavior = CreateFeignDeath(req => Unit.NearbyUnitsInCombatWithMeOrMyStuff.Any( u=> u.Aggro && u.CurrentTargetGuid == Me.Guid), () => TimeSpan.FromMilliseconds(1500), ret => false);

            return new Decorator(
                req => !Unit.IsTrivial(Me.CurrentTarget),
                new PrioritySelector(

                    feignDeathBehavior,

                    // cast Pet survival abilities
                    new Decorator(
                        ret => Me.GotAlivePet && (Pet.HealthPercent < 35 || Pet.HealthPercent < 60 && Unit.NearbyUnfriendlyUnits.Count(u => u.CurrentTargetGuid == Pet.Guid) >= 2),
                        new PrioritySelector(
                            new Decorator(ret => PetManager.CanCastPetAction("Bullheaded"), new Action(ret => PetManager.CastPetAction("Bullheaded"))),
                            new Decorator(ret => PetManager.CanCastPetAction("Last Stand"), new Action(ret => PetManager.CastPetAction("Last Stand")))
                            )
                        ),

                    new Throttle(2, Spell.Buff("Mend Pet", onUnit => Pet, ret => Me.GotAlivePet && Pet.HealthPercent < HunterSettings.MendPetPercent)),

                    // don't worry about wrong pet, only missing or dead pet
                    new Decorator(
                        req => !Me.GotAlivePet,
                        Common.CreateHunterCallPetBehavior(true)
                        ),

                    Spell.Buff("Deterrence",
                        ret => (Me.HealthPercent <= HunterSettings.DeterrenceHealth || HunterSettings.DeterrenceCount <= Unit.NearbyUnfriendlyUnits.Count(u => u.Combat && u.CurrentTargetGuid == Me.Guid && !u.IsPet))),

                    Spell.BuffSelf("Aspect of the Hawk", ret => !Me.IsMoving && !Me.HasAnyAura("Aspect of the Hawk", "Aspect of the Iron Hawk")),

                    new Decorator(
                        ret => SingularRoutine.CurrentWoWContext != WoWContext.Battlegrounds,
                        CreateMisdirectionBehavior()
                        ),

                    // don't use Hunter's Mark in Battlegrounds unless soloing someone
                    /*                        Spell.Buff("Hunter's Mark",
                                        ret => Unit.ValidUnit(Target)
                                            && !TalentManager.HasGlyph("Marked for Death")
                                            && (SingularRoutine.CurrentWoWContext != WoWContext.Battlegrounds || !Unit.NearbyUnfriendlyUnits.Any(u => u.Guid != Target.Guid))
                                            && !Me.CurrentTarget.IsImmune(WoWSpellSchool.Arcane)),
                    */
                    Spell.BuffSelf("Exhilaration", ret => Me.HealthPercent < 35 || (Pet != null && Pet.HealthPercent < 25)),

                    Spell.Buff("Widow Venom", ret => HunterSettings.UseWidowVenom && Target.IsPlayer && Me.IsSafelyFacing(Target) && Target.InLineOfSpellSight),

                    // Buffs - don't stack Bestial Wrath and Rapid Fire
                    Spell.Buff("Bestial Wrath", true, ret => Spell.GetSpellCooldown("Kill Command") == TimeSpan.Zero && !Me.HasAura("Rapid Fire"), "The Beast Within"),

                    Spell.Cast("Stampede",
                        ret => PartyBuff.WeHaveBloodlust
                            || (!Me.IsInGroup() && SafeArea.AllEnemyMobsAttackingMe.Count() > 2)
                            || (Me.GotTarget && Me.CurrentTarget.IsPlayer && Me.CurrentTarget.ToPlayer().IsHostile)),

                    // Level 75 Talents
                    Spell.Cast("A Murder of Crows"),
                    Spell.Cast("Lynx Rush", ret => Pet != null && Unit.NearbyUnfriendlyUnits.Any(u => Pet.Location.Distance(u.Location) <= 10)),

                    // Level 60 Talents
                    Spell.Cast("Dire Beast"),
                    Spell.Cast("Fervor", ctx => Me.CurrentFocus < 50),

                    // Level 90 Talents
                    Spell.Cast("Glaive Toss", req => Me.IsSafelyFacing(Me.CurrentTarget)),
                    Spell.Cast("Powershot", req => Me.IsSafelyFacing(Me.CurrentTarget)),
                    Spell.Cast("Barrage", req => Me.IsSafelyFacing(Me.CurrentTarget)),

                    // for long cooldowns, spend only when worthwhile
                    new Decorator(
                        ret => Pet != null && Target != null && Target.IsAlive
                            && (Target.IsBoss() || Target.IsPlayer || ScaryNPC || 3 <= Unit.NearbyUnfriendlyUnits.Count(u => u.IsTargetingMeOrPet)),
                        new PrioritySelector(
                            Spell.Buff("Rapid Fire", ret => !Me.HasAura("The Beast Within")),
                            Spell.Cast("Rabid", ret => Me.HasAura("The Beast Within")),
                            Spell.Cast("Readiness", ret =>
                            {
                                bool readyForReadiness = true;
                                if (SpellManager.HasSpell("Bestial Wrath"))
                                    readyForReadiness = readyForReadiness && Spell.GetSpellCooldown("Bestial Wrath").TotalSeconds.Between(5, 50);
                                if (SpellManager.HasSpell("Rapid Fire"))
                                    readyForReadiness = readyForReadiness && Spell.GetSpellCooldown("Rapid Fire").TotalSeconds.Between(30, 165);
                                return readyForReadiness;
                            })
                            )
                        ),

                    new Decorator(
                        req => Me.GotAlivePet && PetManager.CanCastPetAction("Reflective Armor Plating") && Unit.NearbyUnfriendlyUnits.Any(u => u.CurrentTargetGuid == Me.Pet.Guid && Me.Pet.IsSafelyFacing(u, 75f)),
                        PetManager.CastAction("Reflective Armor Plating", on => Me.Pet)
                        ),

                    new PrioritySelector(
                        ctx => (!Me.GotAlivePet ? null :
                            Unit.NearbyUnfriendlyUnits
                                .Where(u => u.SpellDistance(Me.Pet) < 30 && Me.Pet.IsSafelyFacing(u))
                                .OrderBy( u => u.SpellDistance(Me.Pet))
                                .FirstOrDefault()),

                        new Decorator(
                            req => ((WoWUnit)req) != null && ((WoWUnit)req).SpellDistance(Me.Pet) < 5,
                            new PrioritySelector(
                                PetManager.CastAction("Sting", on => (WoWUnit) on),
                                PetManager.CastAction("Paralyzing Quill", on => (WoWUnit) on),
                                PetManager.CastAction("Lullaby", on => (WoWUnit) on),
                                PetManager.CastAction("Pummel", on => (WoWUnit) on),
                                PetManager.CastAction("Spore Cloud", on => (WoWUnit) on),
                                PetManager.CastAction("Trample", on => (WoWUnit) on),
                                PetManager.CastAction("Horn Toss", on => (WoWUnit) on)
                                )
                            ),

                        new Decorator(
                            req => ((WoWUnit)req) != null && ((WoWUnit)req).SpellDistance(Me.Pet) < 20,
                            new PrioritySelector(
                                PetManager.CastAction("Sonic Blast", on => (WoWUnit) on),
                                PetManager.CastAction("Petrifying Gaze", on => (WoWUnit) on),
                                PetManager.CastAction("Lullaby", on => (WoWUnit) on),
                                PetManager.CastAction("Bad Manner", on => (WoWUnit) on),
                                PetManager.CastAction("Nether Shock", on => (WoWUnit) on),
                                PetManager.CastAction("Serenity Dust", on => (WoWUnit) on)
                                )
                            ),

                        new Decorator(
                            req => ((WoWUnit)req) != null && ((WoWUnit)req).SpellDistance(Me.Pet) < 30,
                            new PrioritySelector(
                                PetManager.CastAction("Web", on => (WoWUnit)on),
                                PetManager.CastAction("Web Wrap", on => (WoWUnit)on),
                                PetManager.CastAction("Lava Breath", on => (WoWUnit)on)
                                )
                            )
                        )
                    )
                );
        }
Example #3
0
        public static Composite CreateHunterCombatBuffs()
        {
            Composite feignDeathBehavior = new ActionAlwaysFail();

            if ( SingularRoutine.CurrentWoWContext == WoWContext.Normal )
                feignDeathBehavior = CreateFeignDeath( req => NeedFeignDeath, () => TimeSpan.FromSeconds(8), cancel => !Unit.NearbyUnfriendlyUnits.Any(u => u.Distance < 25));

            if ( SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds )
                feignDeathBehavior = CreateFeignDeath(req => NeedFeignDeath, () => TimeSpan.FromSeconds((new Random()).Next(3)), ret => false);

            if ( SingularRoutine.CurrentWoWContext == WoWContext.Instances && HunterSettings.FeignDeathInInstances )
                feignDeathBehavior = CreateFeignDeath(req => Unit.NearbyUnitsInCombatWithMeOrMyStuff.Any( u=> u.Aggro && u.CurrentTargetGuid == Me.Guid), () => TimeSpan.FromMilliseconds(1500), ret => false);

            return new Decorator(
                req => !Unit.IsTrivial(Target),
                new PrioritySelector(

                    !SpellManager.HasSpell("Enhanced Camouflage")
                    ? (Composite)new ActionAlwaysFail()
                    : (Composite)new Sequence(
                        Spell.BuffSelf("Camouflage", req => Me.HealthPercent <= HunterSettings.ImprovedCamouflageHealth),
                        new Wait(
                            1,
                            until => Me.HasAura("Camouflage"),
                            new Action(r => 
                            { 
                                camouflageStart = DateTime.UtcNow;
                                WoWAura aura = Me.GetAllAuras().FirstOrDefault(a => a.Name == "Camouflage");
                                if (aura != null)
                                    camouflageExpires = camouflageStart + aura.TimeLeft;
                                else
                                {
                                    Logger.WriteDiagnostic("Camouflage: cast but never saw buff appear");
                                    camouflageExpires = DateTime.MinValue;
                                }
                            }))
                        ),

                    feignDeathBehavior,

                    // cast Pet survival abilities
                    new Decorator(
                        ret => Me.GotAlivePet && (Pet.HealthPercent < 35 || Pet.HealthPercent < 60 && Unit.NearbyUnfriendlyUnits.Count(u => u.CurrentTargetGuid == Pet.Guid) >= 2),
                        new PrioritySelector(
                            new Decorator(ret => PetManager.CanCastPetAction("Bullheaded"), new Action(ret => PetManager.CastPetAction("Bullheaded"))),
                            new Decorator(ret => PetManager.CanCastPetAction("Last Stand"), new Action(ret => PetManager.CastPetAction("Last Stand")))
                            )
                        ),

                    new Throttle(2, Spell.Buff("Mend Pet", onUnit => Pet, ret => Me.GotAlivePet && Pet.HealthPercent < HunterSettings.MendPetPct)),

                    // don't worry about wrong pet, only missing or dead pet
                    new Decorator(
                        req => !Me.GotAlivePet,
                        Common.CreateHunterCallPetBehavior(true)
                        ),

                    Spell.Buff("Deterrence",
                        ret => (Me.HealthPercent <= HunterSettings.DeterrenceHealth || HunterSettings.DeterrenceCount <= Unit.NearbyUnfriendlyUnits.Count(u => u.Combat && u.CurrentTargetGuid == Me.Guid && !u.IsPet))),

                    new Decorator(
                        ret => SingularRoutine.CurrentWoWContext != WoWContext.Battlegrounds,
                        CreateMisdirectionBehavior()
                        ),

                    new Decorator(
                        req => Me.GotTarget() && Me.IsSafelyFacing(Target) && Target.InLineOfSpellSight,
                        new PrioritySelector(
                            Spell.BuffSelf("Exhilaration", ret => Me.HealthPercent < 35 || (Pet != null && Pet.HealthPercent < 20)),

                            Spell.Buff("Widow Venom", ret => HunterSettings.UseWidowVenom && Target.IsPlayer && Me.IsSafelyFacing(Target) && Target.InLineOfSpellSight),

                            // Buffs - don't stack Bestial Wrath and Rapid Fire
                            Spell.Buff(
                                "Bestial Wrath", 
                                on => Me,
                                req => Spell.GetSpellCooldown("Kill Command") == TimeSpan.Zero && !Me.HasAura("Rapid Fire"), 
                                HasGcd.No, 
                                "The Beast Within"
                                ),


                            Spell.Cast("Stampede",
                                ret => PartyBuff.WeHaveBloodlust
                                    || (!Me.IsInGroup() && SafeArea.AllEnemyMobsAttackingMe.Count() > 2)
                                    || (Me.GotTarget() && Target.IsPlayer && Target.ToPlayer().IsHostile)),

                            // Level 75 Talents
                            Spell.Cast("A Murder of Crows"),

                            // Level 60 Talents
                            Spell.Cast("Dire Beast"),

                            // Level 90 Talents
                            Spell.Cast(
                                "Glaive Toss",
                                on =>
                                {
                                    if (!Me.GotTarget())
                                        return null;

                                    WoWPoint loc = WoWPoint.RayCast(Me.Location, WoWMathHelper.CalculateNeededFacing(Me.Location, Target.Location), 40f);
                                    IEnumerable<WoWUnit> ienum = Clusters.GetPathToPointCluster(loc, Unit.UnfriendlyUnits(44), 44);
                                    int cntCC = 0;
                                    int cntTarget = 0;
                                    int cntNeutral = 0;
                                    WoWUnit target = null;

                                    foreach (WoWUnit u in ienum)
                                    {
                                        cntTarget++;
                                        if (u.IsCrowdControlled())
                                            cntCC++;
                                        if (!u.Combat && !u.IsTrivial() && !u.Aggro && !u.PetAggro && !(u.IsTargetingMeOrPet || u.IsTargetingMyRaidMember))
                                            cntNeutral++;
                                        if (target == null)
                                            target = u;
                                        if (TargetGuid == u.Guid)
                                            target = u;
                                    }

                                    if (cntNeutral > 0)
                                    {
                                        Logger.WriteDebug("Glaive Toss: skipping, {0} additional targets would be pulled", cntNeutral);
                                        return null;
                                    }

                                    if (cntCC > 0)
                                    {
                                        Logger.WriteDebug("Glaive Toss: skipping, {0} crowd controlled targets", cntCC);
                                        return null;
                                    }

                                    if (cntTarget == 0)
                                    {
                                        Logger.WriteDebug("Glaive Toss: skipping, no targets would be hit");
                                        return null;
                                    }

                                    return target;
                                }
                                ),
                            Spell.Cast(
                                "Powershot",
                                on =>
                                {
                                    if (Target == null)
                                        return null;

                                    int dist = (int)Target.Distance + 4;
                                    WoWPoint loc = WoWPoint.RayCast(Me.Location, WoWMathHelper.CalculateNeededFacing(Me.Location, Target.Location), 40f);
                                    IEnumerable<WoWUnit> ienum = Clusters.GetPathToPointCluster(loc, Unit.UnfriendlyUnits(dist), dist);
                                    int cntCC = 0;
                                    int cntTarget = 0;
                                    int cntNeutral = 0;
                                    WoWUnit target = null;

                                    foreach (WoWUnit u in ienum)
                                    {
                                        cntTarget++;
                                        if (u.IsCrowdControlled())
                                            cntCC++;
                                        if (!u.Combat && !u.IsTrivial() && !u.Aggro && !u.PetAggro && !(u.IsTargetingMeOrPet || u.IsTargetingMyRaidMember))
                                            cntNeutral++;
                                        if (target == null)
                                            target = u;
                                        if (TargetGuid == u.Guid)
                                            target = u;
                                    }

                                    if (cntNeutral > 0)
                                    {
                                        Logger.WriteDebug("Powershot: skipping, {0} additional targets would be pulled", cntNeutral);
                                        return null;
                                    }

                                    if (cntCC > 0)
                                    {
                                        Logger.WriteDebug("Powershot: skipping, {0} crowd controlled targets", cntCC);
                                        return null;
                                    }

                                    if (cntTarget == 0)
                                    {
                                        Logger.WriteDebug("Powershot: skipping, no targets would be hit");
                                        return null;
                                    }

                                    return target;
                                }
                                ),
                            Spell.Cast(
                                "Barrage",
                                on =>
                                {
                                    // Does not require CurrentTarget to be non=null
                                    WoWPoint loc = WoWPoint.RayCast(Me.Location, Me.RenderFacing, 30f);
                                    IEnumerable<WoWUnit> ienum = Clusters.GetConeCluster(100f, 46f, Unit.UnfriendlyUnits(50));
                                    int cntCC = 0;
                                    int cntTarget = 0;
                                    int cntNeutral = 0;
                                    WoWUnit target = null;

                                    foreach (WoWUnit u in ienum)
                                    {
                                        cntTarget++;
                                        if (u.IsCrowdControlled())
                                            cntCC++;
                                        if (!u.Combat && !u.IsTrivial() && !u.Aggro && !u.PetAggro && !(u.IsTargetingMeOrPet || u.IsTargetingMyRaidMember))
                                            cntNeutral++;
                                        if (target == null)
                                            target = u;
                                        if (TargetGuid == u.Guid)
                                            target = u;
                                    }

                                    if (cntNeutral > 0)
                                    {
                                        Logger.WriteDebug("Barrage: skipping, {0} additional targets would be pulled", cntNeutral);
                                        return null;
                                    }

                                    if (cntCC > 0)
                                    {
                                        Logger.WriteDebug("Barrage: skipping, {0} crowd controlled targets", cntCC);
                                        return null;
                                    }

                                    if (cntTarget == 0)
                                    {
                                        Logger.WriteDebug("Barrage: skipping, no targets would be hit");
                                        return null;
                                    }

                                    return target;
                                }
                                ),

                            // for long cooldowns, spend only when worthwhile                      
                            new Decorator(
                                ret => Pet != null && Target != null && Target.IsAlive
                                    && (Target.IsBoss() || Target.IsPlayer || ScaryNPC || 3 <= Unit.NearbyUnfriendlyUnits.Count(u => u.IsTargetingMeOrPet)),
                                new PrioritySelector(
                                    Spell.Buff("Rapid Fire", ret => Me.Specialization == WoWSpec.HunterMarksmanship && !Me.HasAura("The Beast Within"))
                                    )
                               ),

                            new Decorator(
                                req => (!Me.GotAlivePet || Me.Pet.HealthPercent < 20)
                                    && PetManager.IsPetUseAllowed
                                    && Me.GotTarget()
                                    && Target.SpellDistance().Between(15, 40),
                                new PrioritySelector(
                // CreateHunterTrapBehavior("Freezing Trap", true, on => Target, ret => SingularRoutine.CurrentWoWContext != WoWContext.Battlegrounds || Me.FocusedUnit == null),
                                    new PrioritySelector(
                                        ctx => WoWMathHelper.CalculatePointFrom(Me.Location, Target.Location, 12f + Target.CombatReach),
                                        CreateHunterTrapBehavior("Ice Trap", loc => (WoWPoint)loc, ret => SingularRoutine.CurrentWoWContext != WoWContext.Battlegrounds || Me.FocusedUnit == null)
                                        )
                                    )
                                )
                            )
                        ),

                    new Decorator(
                        req => Me.GotAlivePet && PetManager.CanCastPetAction("Reflective Armor Plating") && Unit.NearbyUnfriendlyUnits.Any(u => u.CurrentTargetGuid == Me.Pet.Guid && Me.Pet.IsSafelyFacing(u, 75f)),
                        PetManager.CastAction("Reflective Armor Plating", on => Me.Pet)
                        ),

                    new PrioritySelector(
                        ctx => (!Me.GotAlivePet ? null :
                            Unit.NearbyUnfriendlyUnits
                                .Where(u => u.SpellDistance(Me.Pet) < 30 && Me.Pet.IsSafelyFacing(u))
                                .OrderBy(u => u.SpellDistance(Me.Pet))
                                .FirstOrDefault()),

                        new Decorator(
                            req => ((WoWUnit)req) != null && ((WoWUnit)req).SpellDistance(Me.Pet) < 5,
                            new PrioritySelector(
                                PetManager.CastAction("Sting", on => (WoWUnit)on),
                                PetManager.CastAction("Paralyzing Quill", on => (WoWUnit)on),
                                PetManager.CastAction("Lullaby", on => (WoWUnit)on),
                                PetManager.CastAction("Pummel", on => (WoWUnit)on),
                                PetManager.CastAction("Spore Cloud", on => (WoWUnit)on),
                                PetManager.CastAction("Trample", on => (WoWUnit)on),
                                PetManager.CastAction("Horn Toss", on => (WoWUnit)on)
                                )
                            ),

                        new Decorator(
                            req => ((WoWUnit)req) != null && ((WoWUnit)req).SpellDistance(Me.Pet) < 20,
                            new PrioritySelector(
                                PetManager.CastAction("Sonic Blast", on => (WoWUnit)on),
                                PetManager.CastAction("Petrifying Gaze", on => (WoWUnit)on),
                                PetManager.CastAction("Lullaby", on => (WoWUnit)on),
                                PetManager.CastAction("Bad Manner", on => (WoWUnit)on),
                                PetManager.CastAction("Nether Shock", on => (WoWUnit)on),
                                PetManager.CastAction("Serenity Dust", on => (WoWUnit)on)
                                )
                            ),

                        new Decorator(
                            req => ((WoWUnit)req) != null && ((WoWUnit)req).SpellDistance(Me.Pet) < 30,
                            new PrioritySelector(
                                PetManager.CastAction("Web", on => (WoWUnit)on),
                                PetManager.CastAction("Web Wrap", on => (WoWUnit)on),
                                PetManager.CastAction("Lava Breath", on => (WoWUnit)on)
                                )
                            )
                        )
                    )
                );
        }
Example #4
0
        /// <summary>
        /// 
        /// </summary>
        /// <param name="gapCloser"></param>
        /// <returns></returns>
        public static Composite CreateRangedHealerMovementBehavior(Composite gapCloser = null)
        {
            int moveNearTank;
            int stopNearTank;

            if (SingularRoutine.CurrentWoWContext != WoWContext.Instances)
                return new ActionAlwaysFail();

            if (!SingularSettings.Instance.StayNearTank)
                return Movement.CreateMoveBehindTargetBehavior();

            if (gapCloser == null)
                gapCloser = new ActionAlwaysFail();

            bool incombat = (Dynamics.CompositeBuilder.CurrentBehaviorType & BehaviorType.InCombat) != (BehaviorType)0;
            if (incombat)
            {
                moveNearTank = Math.Max(5, SingularSettings.Instance.StayNearTankRangeCombat);
                stopNearTank = Math.Max(moveNearTank / 2, moveNearTank - 5);
            }
            else
            {
                moveNearTank = Math.Max(5, SingularSettings.Instance.StayNearTankRangeRest);
                stopNearTank = Math.Max(moveNearTank / 2, moveNearTank - 5);
            }

            return new PrioritySelector(
                ctx => (Me.Combat && Me.GotTarget() && Unit.ValidUnit(Me.CurrentTarget))
                    ? Me.CurrentTarget
                    : null,
                new Decorator(
                    ret => ret != null,
                    new Sequence(
                        new PrioritySelector(
                            gapCloser,
                            Movement.CreateMoveToLosBehavior(),
                            Movement.CreateMoveToUnitBehavior(on => Me.CurrentTarget, 30, 25),
                            Movement.CreateEnsureMovementStoppedBehavior(25f),
                // to account for Mistweaver Monks facing Soothing Mist target automatically
                            new Decorator(
                                req => !Spell.IsChannelling(),
                                Movement.CreateFaceTargetBehavior()
                                )
                            ),
                        new ActionAlwaysFail()
                        )
                    ),
                new Decorator(
                    ret => ret == null,
                    new Sequence(
                        CreateStayNearTankBehavior(gapCloser),
                        new ActionAlwaysFail()
                        )
                    )
                );
        }
        private static Composite AddCommonBehaviorPrefix( Composite composite, BehaviorType behav)
        {
            if (behav == BehaviorType.LossOfControl)
            {
                composite = new Decorator(
                    ret => HaveWeLostControl,
                    new PrioritySelector(
                        new Action(r =>
                        {
                            if (!StyxWoW.IsInGame)
                            {
                                Logger.WriteDebug(Color.White, "Not in game...");
                                return RunStatus.Success;
                            }

                            return RunStatus.Failure;
                        }),
                        new ThrottlePasses(1, 1, new Decorator(ret => Me.Fleeing, new Action(r => { Logger.Write( LogColor.Hilite, "FLEEING! (loss of control)"); return RunStatus.Failure; }))),
                        new ThrottlePasses(1, 1, new Decorator(ret => Me.Stunned, new Action(r => { Logger.Write( LogColor.Hilite, "STUNNED! (loss of control)"); return RunStatus.Failure; }))),
                        new ThrottlePasses(1, 1, new Decorator(ret => Me.IsSilenced(), new Action(r => { Logger.Write( LogColor.Hilite, "SILENCED! (loss of control)"); return RunStatus.Failure; }))),
                        new Throttle(1,
                            new PrioritySelector(
                                composite ?? new ActionAlwaysFail(),
                                new Decorator(
                                    ret => SingularSettings.Instance.UseRacials,
                                    new PrioritySelector(
                                        Spell.Cast("Will of the Forsaken", on => Me, ret => Me.Race == WoWRace.Undead && Me.Fleeing),
                                        Spell.Cast("Every Man for Himself", on => Me, ret => Me.Race == WoWRace.Human && (Me.Stunned || Me.Fleeing))
                                        )
                                    ),

                                Item.UseEquippedTrinket(TrinketUsage.CrowdControlled),
                                Item.UseEquippedTrinket(TrinketUsage.CrowdControlledSilenced)
                                )
                            ),
                        new ActionAlwaysSucceed()
                        )
                    );
            }

            if (behav == BehaviorType.Rest)
            {
                Composite combatInRestCheck;
                if (!SingularSettings.Debug)
                    combatInRestCheck = new ActionAlwaysFail();
                else
                    combatInRestCheck = new Throttle(
                        1,
                        new ThrottlePasses(
                            1,
                            TimeSpan.FromSeconds(1),
                            RunStatus.Failure,
                            new Decorator(
                                req => Me.IsActuallyInCombat,
                                new Sequence(
                                    new PrioritySelector(
                                        new ThrottlePasses(
                                            1, 5,
                                            new Action(r => Logger.Write(Color.Yellow, "Bot Error: {0} or plugin called Rest behavior while in combat", SingularRoutine.GetBotName()))
                                            ),
                                        new Action(r => Logger.WriteDebug(Color.Yellow, "Bot Error: {0} or plugin called Rest behavior while in combat", SingularRoutine.GetBotName()))
                                        ),
                                    new ActionAlwaysFail()
                                    )
                                )
                            )
                        );

                composite = new LockSelector(
                    new CallWatch("Rest",
                        new Decorator(
                            ret => !Me.IsFlying && AllowBehaviorUsage() && !SingularSettings.Instance.DisableNonCombatBehaviors,
                            new PrioritySelector(

                                // TestDynaWait(),

                                // following is to allow cancelling of kiting in progress
                                new Decorator(
                                    ret => Kite.IsKitingActive(),
                                    new HookExecutor(HookName("KitingBehavior"))
                                    ),

                                new Decorator(
                                    req => ShouldWeFightDuringRest(),
                                    new PrioritySelector(
                                        Safers.EnsureTarget(),
                                        new Decorator(
                                            req => Unit.ValidUnit(Me.CurrentTarget) && Me.CurrentTarget.SpellDistance() < 45,
                                            new PrioritySelector(
                                                new ThrottlePasses(
                                                    1, TimeSpan.FromSeconds(15), RunStatus.Failure,
                                                    new Action(r => Logger.WriteDiagnostic(LogColor.Hilite, "Forcing combat from Rest Behavior")),
                                                    new ActionAlwaysFail()
                                                    ),
                                                SingularRoutine.Instance.HealBehavior,
                                                SingularRoutine.Instance.CombatBuffBehavior,
                                                SingularRoutine.Instance.CombatBehavior,
                                                new ActionAlwaysSucceed()
                                                )
                                            )
                                        )
                                    ),

                                // new Action(r => { _guidLastTarget = 0; return RunStatus.Failure; }),
                                new Decorator(
                                    req => !OkToCallBehaviorsWithCurrentCastingStatus(allow: LagTolerance.No),
                                    new ActionAlwaysSucceed()
                                    ),

                                // lost control in Rest -- force a RunStatus.Failure so we don't loop in Rest
                                new Sequence(
                                    SingularRoutine.Instance._lostControlBehavior,
                                    new ActionAlwaysFail()
                                    ),

                                // skip Rest logic if we lost control (since we had to return Fail to prevent Rest loop)
                                new Decorator(
                                    req => !HaveWeLostControl,
                                    composite ?? new ActionAlwaysFail()
                                    )
                                )
                            )
                        )
                    );
            }

            if (behav == BehaviorType.PreCombatBuffs)
            {
                Composite precombatInCombatCheck;
                if (!SingularSettings.Debug)
                    precombatInCombatCheck = new ActionAlwaysFail();
                else
                    precombatInCombatCheck = new Throttle(
                        1,
                        new ThrottlePasses(
                            1,
                            TimeSpan.FromSeconds(1),
                            RunStatus.Failure,
                            new Decorator(
                                req => Me.IsActuallyInCombat,
                                new Sequence(
                                    new PrioritySelector(
                                        new ThrottlePasses(
                                            1, 5,
                                            new Action(r => Logger.Write(Color.Yellow, "Bot Error: {0} or plugin called PreCombatBuff behavior while in combat", SingularRoutine.GetBotName()))
                                            ),
                                        new Action(r => Logger.WriteDebug(Color.Yellow, "Bot Error: {0} or plugin called PreCombatBuff behavior while in combat", SingularRoutine.GetBotName()))
                                        ),
                                    new ActionAlwaysFail()
                                    )
                                )
                            )
                        );

                composite = new LockSelector(
                    new CallWatch("PreCombat",
                        new Decorator(  // suppress non-combat buffing if standing around waiting on DungeonBuddy or BGBuddy queues
                            ret => !Me.Mounted
                                && !Me.HasAnyAura("Darkflight")
                                && !SingularSettings.Instance.DisableNonCombatBehaviors
                                && AllowNonCombatBuffing(),
                            new PrioritySelector(
                                new Decorator(
                                    req => !OkToCallBehaviorsWithCurrentCastingStatus(),
                                    Spell.WaitForGcdOrCastOrChannel()
                                    ),
                                Helpers.Common.CreateUseTableBehavior(),
                                Helpers.Common.CreateUseSoulwellBehavior(),

                                Item.CreateUseFlasksBehavior(),
                                Item.CreateUseScrollsBehavior(),

                                composite ?? new ActionAlwaysFail()
                                )
                            )
                        )
                    );
            }

            if (behav == BehaviorType.PullBuffs)
            {
                composite = new LockSelector(
                    new CallWatch("PullBuffs",
                        new Decorator(
                            ret => AllowBehaviorUsage() && OkToCallBehaviorsWithCurrentCastingStatus(),
                            composite ?? new ActionAlwaysFail()
                            )
                        )
                    );
            }

            if (behav == BehaviorType.Pull)
            {
                composite = new LockSelector(
                    new CallWatch("Pull",
                        new Decorator(
                            ret => AllowBehaviorUsage(), // && (!Me.GotTarget() || !Blacklist.Contains(Me.CurrentTargetGuid, BlacklistFlags.Combat)),
                            new PrioritySelector(
                                new Decorator(
                                    ret => !HotkeyDirector.IsCombatEnabled,
                                    new ActionAlwaysSucceed()
                                    ),
            #if BOTS_NOT_CALLING_PULLBUFFS
                            _pullBuffsBehavior,
            #endif
                                CreateLogTargetChanges(BehaviorType.Pull, "<<< PULL >>>"),
                                Item.CreateThunderLordGrappleBehavior(),
                                composite ?? new ActionAlwaysFail()
                                )
                            )
                        )
                    );
            }

            if (behav == BehaviorType.Heal)
            {
                Composite behavHealingSpheres = new ActionAlwaysFail();
                if (SingularSettings.Instance.MoveToSpheres)
                {
                    behavHealingSpheres = new ThrottlePasses(
                        1, 1,
                        new Decorator(
                            ret => Me.HealthPercent < SingularSettings.Instance.SphereHealthPercentInCombat && Singular.ClassSpecific.Monk.Common.AnySpheres(SphereType.Life, SingularSettings.Instance.SphereDistanceInCombat),
                            Singular.ClassSpecific.Monk.Common.CreateMoveToSphereBehavior(SphereType.Life, SingularSettings.Instance.SphereDistanceInCombat)
                            )
                        );
                }

                composite = new LockSelector(
                    new CallWatch("Heal",

                        // following must occur before any cast or movement during Combat
                        Generic.CreateCancelShadowmeld(),

                        SingularRoutine.Instance._lostControlBehavior,
                        new Decorator(
                            ret => Kite.IsKitingActive(),
                            new HookExecutor(HookName("KitingBehavior"))
                            ),
                        new Decorator(
                            ret => AllowBehaviorUsage() && OkToCallBehaviorsWithCurrentCastingStatus(),
                            new PrioritySelector(
                                Generic.CreatePotionAndHealthstoneBehavior(),
                                composite ?? new ActionAlwaysFail(),
                                behavHealingSpheres
                                )
                            )
                        )
                    );
            }

            if (behav == BehaviorType.CombatBuffs)
            {
                composite = new LockSelector(
                    new CallWatch("CombatBuffs",
                        new Decorator(
                            ret => AllowBehaviorUsage() && OkToCallBehaviorsWithCurrentCastingStatus(),
                            new PrioritySelector(
                                new Decorator(ret => !HotkeyDirector.IsCombatEnabled, new ActionAlwaysSucceed()),
                                Generic.CreateUseTrinketsBehaviour(),
                                Generic.CreatePotionAndHealthstoneBehavior(),
                                Item.CreateUseXPBuffPotionsBehavior(),
                                Generic.CreateRacialBehaviour(),
                                Generic.CreateGarrisonAbilityBehaviour(),
                                composite ?? new ActionAlwaysFail()
                                )
                            )
                        )
                    );
            }

            if (behav == BehaviorType.Combat)
            {
                composite = new LockSelector(
                    new CallWatch("Combat",
                        new Decorator(
                            ret => AllowBehaviorUsage(), // && (!Me.GotTarget() || !Blacklist.Contains(Me.CurrentTargetGuid, BlacklistFlags.Combat)),
                            new PrioritySelector(
                                new Decorator(
                                    ret => !HotkeyDirector.IsCombatEnabled,
                                    new ActionAlwaysSucceed()
                                    ),
                                CreatePullMorePull(),
                                CreateLogTargetChanges(BehaviorType.Combat, "<<< ADD >>>"),
                                composite ?? new ActionAlwaysFail()
                                )
                            )
                        )
                    );
            }

            if (behav == BehaviorType.Death)
            {
                composite = new LockSelector(
                    new CallWatch("Death",
                        new Decorator(
                            ret => AllowBehaviorUsage(),
                            new PrioritySelector(
                                new Action(r => { ResetCurrentTarget(); return RunStatus.Failure; }),
                                new Decorator(
                                    req => !OkToCallBehaviorsWithCurrentCastingStatus(),
                                    Spell.WaitForGcdOrCastOrChannel()
                                    ),
                                composite ?? new ActionAlwaysFail()
                                )
                            )
                        )
                    );
            }

            return composite;
        }
Example #6
0
        public static Composite CreateShamanDpsHealBehavior()
        {
            Composite offheal;
            if (!ShamanSettings.AllowOffHealHeal)
                offheal = new ActionAlwaysFail();
            else
            {
                offheal = new Decorator(
                    ret => NeedToOffHealSomeone,
                    Restoration.CreateRestoShamanHealingOnlyBehavior(selfOnly: false)
                    );
            }

            return new Decorator(
                ret => !Spell.IsGlobalCooldown() && !Spell.IsCastingOrChannelling(),
                new PrioritySelector(
            // use predicted health for non-combat healing to reduce drinking downtime and help
            // .. avoid unnecessary heal casts
                    new Decorator(
                        ret => !Me.Combat,
                        Spell.Cast("Healing Surge", ret => Me, ret => Me.GetPredictedHealthPercent(true) < 85)
                        ),

                    new Decorator(
                        ret => Me.Combat && !Spell.IsGlobalCooldown(),

                        new PrioritySelector(

                            Spell.OffGCD(
                                Spell.BuffSelf("Ancestral Guidance",
                                    ret => Me.HealthPercent < ShamanSettings.SelfAncestralGuidance
                                        && Me.GotTarget
                                        && Me.CurrentTarget.TimeToDeath() > 8
                                    )
                                ),

                            // save myself if possible
                            new Decorator(
                                ret => (!Me.IsInGroup() || Battlegrounds.IsInsideBattleground)
                                    && Me.HealthPercent < ShamanSettings.SelfAncestralSwiftnessHeal,
                                new Sequence(
                                    Spell.BuffSelf("Ancestral Swiftness"),
                                    new Sequence(
                                        Spell.Cast("Greater Healing Wave", ret => Me),
                                        Spell.Cast("Healing Surge", ret => Me)
                                        )
                                    )
                                ),

                            // include optional offheal behavior
                            offheal,

                            // use non-predicted health as a trigger for totems
                            new Decorator(
                                ret => !Common.AnyHealersNearby,
                                new PrioritySelector(
                                    Spell.BuffSelf(
                                        "Healing Tide Totem",
                                        ret => !Me.IsMoving
                                            && Unit.GroupMembers.Any(
                                                p => p.HealthPercent < ShamanSettings.HealingTideTotemPercent
                                                    && p.Distance <= Totems.GetTotemRange(WoWTotem.HealingTide))),
                                    Spell.BuffSelf( "Healing Stream Totem",
                                        ret => !Me.IsMoving
                                            && !Totems.Exist(WoWTotemType.Water)
                                            && Unit.GroupMembers.Any(
                                                p => p.HealthPercent < ShamanSettings.SelfHealingStreamTotem
                                                    && p.Distance <= Totems.GetTotemRange(WoWTotem.HealingTide))),

                                    // use actual health for following, not predicted as its a low health value
                                    // .. and its okay for multiple heals at that point
                                    Spell.Cast(
                                        "Healing Surge",
                                        mov => true,
                                        on => Me,
                                        req => Me.GetPredictedHealthPercent(true) <= ShamanSettings.SelfHealingSurge,
                                        cancel => Me.HealthPercent > 90
                                        )
                                    )
                                )
                            )
                        )
                    )
                );
        }
Example #7
0
        /// <summary>
        /// create standard Avoidance behavior (disengage and/or kiting)
        /// </summary>
        /// <param name="disengageSpell">spellName to use for disengage</param>
        /// <param name="disengageDist">distance spellName will jump</param>
        /// <param name="disengageDir">direction spellName jumps</param>
        /// <param name="crowdControl">behavior called to crowd control melee enemies or halt use of disengage</param>
        /// <param name="needDisengage">delegate to check for using disgenage. defaults checking settings for health and # attackers</param>
        /// <param name="needKiting">delegate to check for using kiting. defaults to checking settings for health and # attackers</param>
        /// <param name="cancelKiting">delegate to check if kiting should be cancelled. defaults to checking no attackers that aren't crowd controlled</param>
        /// <returns></returns>
        public static Composite CreateAvoidanceBehavior(
            string disengageSpell,
            int disengageDist,
            Disengage.Direction disengageDir,
            Composite crowdControl,
            SimpleBooleanDelegate needDisengage = null, 
            SimpleBooleanDelegate needKiting = null, 
            SimpleBooleanDelegate cancelKiting = null
            )
        {
            PrioritySelector pri = new PrioritySelector();

            // build default check for whether disengage is needed
            if (needDisengage == null)
            {
                needDisengage = req =>
                {
                    if (disengageSpell == null || disengageDir == Disengage.Direction.None)
                        return false;

                    if (!Kite.IsDisengageWantedByUserSettings() || !MovementManager.IsClassMovementAllowed || !SingularRoutine.IsAllowed(CapabilityFlags.Kiting))
                        return false;

                    if (Spell.IsSpellOnCooldown(disengageSpell) && (!SingularSettings.Instance.UseRacials || Spell.IsSpellOnCooldown("Rocket Jump")))
                        return false;

                    bool useDisengage = false;

                    if (SingularRoutine.CurrentWoWContext == WoWContext.Normal)
                    {
                        int countMelee = Unit.UnitsInCombatWithUsOrOurStuff(7).Sum( u => !u.IsPlayer || !u.IsMelee() ? 1 : SingularSettings.Instance.DisengageMobCount);
                        if (countMelee >= SingularSettings.Instance.DisengageMobCount)
                            useDisengage = true;
                        else if (Me.HealthPercent <= SingularSettings.Instance.DisengageHealth && countMelee > 0)
                            useDisengage = true;
                    }
                    else if (SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds)
                    {
                        useDisengage = Unit.UnfriendlyUnits(7).Any(u => u.IsMelee());
                    }

                    return useDisengage;
                };
            }

            // standard disengage behavior, with class specific check to make sure parties are crowd controlled
            if (SingularSettings.Instance.DisengageAllowed)
            {
                Composite behavRocketJump = new ActionAlwaysFail();
                if (SingularSettings.Instance.UseRacials && SpellManager.HasSpell("Rocket Jump"))
                    behavRocketJump = Disengage.CreateDisengageBehavior("Rocket Jump", 20, Disengage.Direction.Frontwards, null);

                pri.AddChild(
                    new Decorator(
                        ret => needDisengage(ret),
                        new Sequence(
                            crowdControl,   // return Failure if shouldnt disengage, otherwise Success to allow
                            new PrioritySelector(
                                // new Action(r => { Logger.Write( LogColor.Hilite, "^Avoidance: disengaging away from mobs!"); return RunStatus.Failure; } ),
                                Disengage.CreateDisengageBehavior(disengageSpell, disengageDist, disengageDir, null),
                                behavRocketJump
                                )
                            )
                        )
                    );
            }

            // build default needKiting check
            if (needKiting == null)
            {
                needKiting = req =>
                {
                    if (!Kite.IsKitingWantedByUserSettings() || !MovementManager.IsClassMovementAllowed || !SingularRoutine.IsAllowed(CapabilityFlags.Kiting))
                        return false;

                    bool useKiting = false;
                    int countMelee = Unit.UnitsInCombatWithUsOrOurStuff(7).Count();
                    if (countMelee >= SingularSettings.Instance.KiteMobCount)
                        useKiting = true;
                    else if (Me.HealthPercent <= SingularSettings.Instance.KiteHealth && countMelee > 0)
                        useKiting = true;

                    return useKiting;
                };
            }

            if (cancelKiting == null)
            {
                cancelKiting = req =>
                {
                    int countAttackers = Unit.UnitsInCombatWithUsOrOurStuff(7).Count();
                    return (countAttackers == 0);
                };
            }

            // add check to initiate kiting behavior
            if (SingularSettings.Instance.KiteAllow)
            {
                pri.AddChild(
                    new Decorator(
                        ret => needKiting(ret),
                        new Sequence(
                            crowdControl,
                            Kite.BeginKitingBehavior()
                            )
                        )
                    );
            }

            if (!pri.Children.Any())
            {
                return new ActionAlwaysFail();
            }

            return new Decorator(req => MovementManager.IsClassMovementAllowed, pri);
        }
        public static Composite MoveBehaviorInlineToCombat( BehaviorType bt )
        {
            string hookNameOrig = HookName(bt);
            string hookNameInline = hookNameOrig + "-INLINE";

            if (BehaviorType.Combat != Dynamics.CompositeBuilder.CurrentBehaviorType)
            {
                Logger.WriteDiagnostic("MoveBehaviorInline: suppressing Inline for {0} behavior", Dynamics.CompositeBuilder.CurrentBehaviorType);
                return new ActionAlwaysFail();
            }

            if (Instance == null)
            {
                StopBot(string.Format("MoveBehaviorInline: PROGRAM ERROR - SingularRoutine.Instance not initialized yet for {0} !!!!", bt));
                return null;
            }

            if (bt == Dynamics.CompositeBuilder.CurrentBehaviorType)
            {
                StopBot(string.Format("MoveBehaviorInline: PROGRAM ERROR - referenced behavior({0}) == current behavior({1}) !!!!", bt, bt));
                return null;
            }

            // on creation, move hook composite (if exists) from default to inline hook
            Composite composite = new ActionAlwaysFail();
            if (TreeHooks.Instance.Hooks.ContainsKey(hookNameOrig))
            {
                if (TreeHooks.Instance.Hooks[hookNameOrig].Count() == 1)
                {
                    composite = TreeHooks.Instance.Hooks[hookNameOrig].First().Composite;
                    if (composite == null)
                    {
                        StopBot(string.Format("MoveBehaviorInline: PROGRAM ERROR - not composite for behavior({0}) !!!!", bt));
                        return null;
                    }

                    TreeHooks.Instance.ReplaceHook(hookNameInline, composite);
                    TreeHooks.Instance.RemoveHook(hookNameOrig, composite);
                    Logger.WriteFile("MoveBehaviorInline: moving {0} behavior within {1} {2}", bt, Dynamics.CompositeBuilder.CurrentBehaviorName, Dynamics.CompositeBuilder.CurrentBehaviorPriority);
                }
            }

            return new HookExecutor(hookNameInline);
        }
Example #9
0
        public static Composite CreateMonkDpsHealBehavior()
        {
            Composite offheal;
            if (!SingularSettings.Instance.DpsOffHealAllowed)
                offheal = new ActionAlwaysFail();
            else
            {
                offheal = new Decorator(
                    ret => HealerManager.ActingAsOffHealer,
                    CreateMonkOffHealBehavior()
                    );
            }

            return new Decorator(
                ret => !Spell.IsGlobalCooldown() && !Spell.IsCastingOrChannelling(),
                new PrioritySelector(

                    new Decorator(
                        ret => !Me.Combat
                            && !Me.IsMoving
                            && Me.HealthPercent <= 85  // not redundant... this eliminates unnecessary GetPredicted... checks
                            && SpellManager.HasSpell("Surging Mist")
                            && Me.PredictedHealthPercent(includeMyHeals: true) < 85,
                        new PrioritySelector(
                            new Sequence(
                                ctx => (float)Me.HealthPercent,
                                new Action(r => Logger.WriteDebug("Surging Mist: {0:F1}% Predict:{1:F1}% and moving:{2}, cancast:{3}", (float)r, Me.PredictedHealthPercent(includeMyHeals: true), Me.IsMoving, Spell.CanCastHack("Surging Mist", Me, skipWowCheck: false))),
                                Spell.Cast(
                                    "Surging Mist",
                                    mov => true,
                                    on => Me,
                                    req => true,
                                    cancel => Me.HealthPercent > 85
                                    ),
                                new WaitContinue(TimeSpan.FromMilliseconds(500), until => !Me.IsCasting && Me.HealthPercent > (1.1 * ((float)until)), new ActionAlwaysSucceed()),
                                new Action(r => Logger.WriteDebug("Surging Mist: After Heal Attempted: {0:F1}% Predicted: {1:F1}%", Me.HealthPercent, Me.PredictedHealthPercent(includeMyHeals: true)))
                                ),
                            Spell.Buff( "Expel Harm", on => Me, req => Me.HealthPercent < MonkSettings.ExpelHarmHealth)
                            )
                        ),

                    new Decorator(
                        ret => Me.Combat,

                        new PrioritySelector(

                            // add buff / shield here
                            Spell.HandleOffGCD(
                                new Throttle(
                                    1,
                                    Spell.Cast("Tigereye Brew", ctx => Me, ret => Me.HealthPercent < MonkSettings.TigereyeBrewHealth && Me.HasAura("Tigereye Brew", 1)),
                                    Spell.BuffSelf("Dampen Harm", req => Me.HealthPercent < MonkSettings.DampenHarmPct || MonkSettings.DampenHarmCount <= Unit.UnfriendlyUnits(40).Count( u => u.IsAlive && u.CurrentTargetGuid == Me.Guid && !u.IsTrivial()))
                                    )
                                ),

                            // save myself if possible
                            new Decorator(
                                ret => (!Me.IsInGroup() || Battlegrounds.IsInsideBattleground)
                                    && !Me.IsMoving
                                    && Me.HealthPercent < MonkSettings.SurgingMist
                                    && Me.PredictedHealthPercent(includeMyHeals: true) < MonkSettings.SurgingMist,
                                new PrioritySelector(
                                    new Sequence(
                                        ctx => (float)Me.HealthPercent,
                                        new Action(r => Logger.WriteDebug("Surging Mist: {0:F1}% Predict:{1:F1}% and moving:{2}, cancast:{3}", (float)r, Me.PredictedHealthPercent(includeMyHeals: true), Me.IsMoving, Spell.CanCastHack("Surging Mist", Me, skipWowCheck: false))),
                                        Spell.Cast(
                                            "Surging Mist",
                                            mov => true,
                                            on => Me,
                                            req => true,
                                            cancel => Me.HealthPercent > 85
                                            ),
                                        new WaitContinue(TimeSpan.FromMilliseconds(500), until => !Me.IsCasting && Me.HealthPercent > (1.1 * ((float)until)), new ActionAlwaysSucceed()),
                                        new Action(r => Logger.WriteDebug("Surging Mist: After Heal Attempted: {0:F1}% Predicted: {1:F1}%", Me.HealthPercent, Me.PredictedHealthPercent(includeMyHeals: true)))
                                        ),
                                    new Action(r => Logger.WriteDebug("Surging Mist: After Heal Skipped: {0:F1}% Predicted: {1:F1}%", Me.HealthPercent, Me.PredictedHealthPercent(includeMyHeals: true)))
                                    )
                                )
                            )
                        ),

                    offheal
                    )
                );
        }
Example #10
0
        public static Composite CreateShamanDpsHealBehavior()
        {
            Composite offheal;

            if (!SingularSettings.Instance.DpsOffHealAllowed)
            {
                offheal = new ActionAlwaysFail();
            }
            else
            {
                offheal = new Decorator(
                    ret => HealerManager.ActingAsOffHealer,
                    CreateDpsShamanOffHealBehavior()
                    );
            }

            return(new Decorator(
                       ret => !Spell.IsGlobalCooldown() && !Spell.IsCastingOrChannelling(),
                       new PrioritySelector(

                           new Decorator(
                               ret => !Me.Combat &&
                               (!Me.IsMoving || Me.HasAura("Maelstrom Weapon", 5)) &&
                               Me.HealthPercent <= 85 && // not redundant... this eliminates unnecessary GetPredicted... checks
                               SpellManager.HasSpell("Healing Surge") &&
                               Me.PredictedHealthPercent(includeMyHeals: true) < 85,
                               new PrioritySelector(
                                   new Sequence(
                                       ctx => (float)Me.HealthPercent,
                                       new Action(r => Logger.WriteDebug("Healing Surge: {0:F1}% Predict:{1:F1}% and moving:{2}, cancast:{3}", (float)r, Me.PredictedHealthPercent(includeMyHeals: true), Me.IsMoving, Spell.CanCastHack("Healing Surge", Me, skipWowCheck: false))),
                                       Spell.Cast(
                                           "Healing Surge",
                                           mov => true,
                                           on => Me,
                                           req => true,
                                           cancel => Me.HealthPercent > 85
                                           ),
                                       new WaitContinue(TimeSpan.FromMilliseconds(500), until => !Me.IsCasting && Me.HealthPercent > (1.1 * ((float)until)), new ActionAlwaysSucceed()),
                                       new Action(r => Logger.WriteDebug("Healing Surge: After Heal Attempted: {0:F1}% Predicted: {1:F1}%", Me.HealthPercent, Me.PredictedHealthPercent(includeMyHeals: true)))
                                       ),
                                   new Action(r => Logger.WriteDebug("Healing Surge: After Heal Skipped: {0:F1}% Predicted: {1:F1}%", Me.HealthPercent, Me.PredictedHealthPercent(includeMyHeals: true)))
                                   )
                               ),

                           new Decorator(
                               ret => Me.Combat,

                               new PrioritySelector(

                                   Spell.BuffSelf("Astral Shift", ret => Me.HealthPercent < ShamanSettings.AstralShiftPercent || Common.StressfulSituation),
                                   Spell.BuffSelf(WoWTotem.StoneBulwark.ToSpellId(), ret => !Me.IsMoving && (Common.StressfulSituation || Me.HealthPercent < ShamanSettings.StoneBulwarkTotemPercent && !Totems.Exist(WoWTotem.EarthElemental))),
                                   Spell.BuffSelf("Shamanistic Rage", ret => Me.HealthPercent < ShamanSettings.ShamanisticRagePercent || Common.StressfulSituation),

                                   Spell.HandleOffGCD(
                                       Spell.BuffSelf("Ancestral Guidance",
                                                      ret => Me.HealthPercent < ShamanSettings.SelfAncestralGuidance &&
                                                      Me.GotTarget() &&
                                                      Me.CurrentTarget.TimeToDeath() > 8
                                                      )
                                       ),

                                   new Decorator(
                                       req => !Me.IsMoving && ShamanSettings.SelfHealingStreamTotem > 0 && !Totems.Exist(WoWTotemType.Water),
                                       Spell.BuffSelf(
                                           WoWTotem.HealingStream.ToSpellId(),
                                           req =>
            {
                int count = Unit.NearbyUnitsInCombatWithMeOrMyStuff.Count(u => !u.IsTrivial());
                if (count > 1)
                {
                    return true;
                }
                if (count > 0 && Me.HealthPercent < ShamanSettings.SelfHealingStreamTotem)
                {
                    if (!Me.GotTarget() || Me.CurrentTarget.IsPlayer || Me.CurrentTarget.TimeToDeath() > 5)
                    {
                        return true;
                    }
                    if (Me.HealthPercent < (ShamanSettings.SelfHealingStreamTotem / 2))
                    {
                        return true;
                    }
                }
                return false;
            }
                                           )
                                       ),

                                   // save myself if possible
                                   new Decorator(
                                       ret => (!Me.IsInGroup() || Battlegrounds.IsInsideBattleground) &&
                                       (!Me.IsMoving || Me.HasAura("Maelstrom Weapon", 5) || !Spell.IsSpellOnCooldown("Ancestral Swiftness")) &&
                                       Me.HealthPercent < ShamanSettings.SelfAncestralSwiftnessHeal &&
                                       Me.PredictedHealthPercent(includeMyHeals: true) < ShamanSettings.SelfAncestralSwiftnessHeal,
                                       new PrioritySelector(
                                           Spell.HandleOffGCD(Spell.BuffSelf("Ancestral Swiftness")),
                                           new PrioritySelector(
                                               new Sequence(
                                                   ctx => (float)Me.HealthPercent,
                                                   new Action(r => Logger.WriteDebug("Healing Surge: {0:F1}% Predict:{1:F1}% and moving:{2}, cancast:{3}", (float)r, Me.PredictedHealthPercent(includeMyHeals: true), Me.IsMoving, Spell.CanCastHack("Healing Surge", Me, skipWowCheck: false))),
                                                   Spell.Cast(
                                                       "Healing Surge",
                                                       mov => true,
                                                       on => Me,
                                                       req => true,
                                                       cancel => Me.HealthPercent > 85
                                                       ),
                                                   new WaitContinue(TimeSpan.FromMilliseconds(500), until => !Me.IsCasting && Me.HealthPercent > (1.1 * ((float)until)), new ActionAlwaysSucceed()),
                                                   new Action(r => Logger.WriteDebug("Healing Surge: After Heal Attempted: {0:F1}% Predicted: {1:F1}%", Me.HealthPercent, Me.PredictedHealthPercent(includeMyHeals: true)))
                                                   ),
                                               new Action(r => Logger.WriteDebug("Healing Surge: After Heal Skipped: {0:F1}% Predicted: {1:F1}%", Me.HealthPercent, Me.PredictedHealthPercent(includeMyHeals: true)))
                                               )
                                           )
                                       )
                                   )
                               ),

                           offheal
                           )
                       ));
        }
Example #11
0
        public static Composite CreateShamanDpsHealBehavior()
        {
            Composite offheal;
            if (!SingularSettings.Instance.DpsOffHealAllowed)
                offheal = new ActionAlwaysFail();
            else
            {
                offheal = new Decorator(
                    ret => HealerManager.ActingAsOffHealer,
                    CreateDpsShamanOffHealBehavior()
                    );
            }

            return new Decorator(
                ret => !Spell.IsGlobalCooldown() && !Spell.IsCastingOrChannelling(),
                new PrioritySelector(

                    new Decorator(
                        ret => !Me.Combat
                            && (!Me.IsMoving || Me.HasAura("Maelstrom Weapon", 5))
                            && Me.HealthPercent <= 85  // not redundant... this eliminates unnecessary GetPredicted... checks
                            && SpellManager.HasSpell("Healing Surge")
                            && Me.PredictedHealthPercent(includeMyHeals: true) < 85,
                        new PrioritySelector(
                            new Sequence(
                                ctx => (float)Me.HealthPercent,
                                new Action(r => Logger.WriteDebug("Healing Surge: {0:F1}% Predict:{1:F1}% and moving:{2}, cancast:{3}", (float) r, Me.PredictedHealthPercent(includeMyHeals: true), Me.IsMoving, Spell.CanCastHack("Healing Surge", Me, skipWowCheck: false))),
                                Spell.Cast(
                                    "Healing Surge",
                                    mov => true,
                                    on => Me,
                                    req => true,
                                    cancel => Me.HealthPercent > 85
                                    ),
                                new WaitContinue(TimeSpan.FromMilliseconds(500), until => !Me.IsCasting && Me.HealthPercent > (1.1 * ((float)until)), new ActionAlwaysSucceed()),
                                new Action( r => Logger.WriteDebug("Healing Surge: After Heal Attempted: {0:F1}% Predicted: {1:F1}%", Me.HealthPercent, Me.PredictedHealthPercent(includeMyHeals: true)))
                                ),
                            new Action( r => Logger.WriteDebug("Healing Surge: After Heal Skipped: {0:F1}% Predicted: {1:F1}%", Me.HealthPercent, Me.PredictedHealthPercent(includeMyHeals: true)))
                            )
                        ),

                    new Decorator(
                        ret => Me.Combat,

                        new PrioritySelector(

                            Spell.BuffSelf("Astral Shift", ret => Me.HealthPercent < ShamanSettings.AstralShiftPercent || Common.StressfulSituation),
                            Spell.BuffSelf(WoWTotem.StoneBulwark.ToSpellId(), ret => !Me.IsMoving && (Common.StressfulSituation || Me.HealthPercent < ShamanSettings.StoneBulwarkTotemPercent && !Totems.Exist(WoWTotem.EarthElemental))),
                            Spell.BuffSelf("Shamanistic Rage", ret => Me.HealthPercent < ShamanSettings.ShamanisticRagePercent || Common.StressfulSituation),

                            Spell.HandleOffGCD(
                                Spell.BuffSelf("Ancestral Guidance",
                                    ret => Me.HealthPercent < ShamanSettings.SelfAncestralGuidance
                                        && Me.GotTarget()
                                        && Me.CurrentTarget.TimeToDeath() > 8
                                    )
                                ),

                            new Decorator(
                                req => !Me.IsMoving && ShamanSettings.SelfHealingStreamTotem > 0 && !Totems.Exist(WoWTotemType.Water),
                                Spell.BuffSelf(
                                    WoWTotem.HealingStream.ToSpellId(),
                                    req =>
                                    {
                                        int count = Unit.NearbyUnitsInCombatWithMeOrMyStuff.Count(u => !u.IsTrivial());
                                        if (count > 1)
                                            return true;
                                        if (count > 0 && Me.HealthPercent < ShamanSettings.SelfHealingStreamTotem)
                                        {
                                            if (!Me.GotTarget() || Me.CurrentTarget.IsPlayer || Me.CurrentTarget.TimeToDeath() > 5)
                                            {
                                                return true;
                                            }
                                            if (Me.HealthPercent < (ShamanSettings.SelfHealingStreamTotem / 2))
                                            {
                                                return true;
                                            }
                                        }
                                        return false;
                                    }
                                    )
                                ),

                            // save myself if possible
                            new Decorator(
                                ret => (!Me.IsInGroup() || Battlegrounds.IsInsideBattleground)
                                    && (!Me.IsMoving || Me.HasAura("Maelstrom Weapon", 5) || !Spell.IsSpellOnCooldown("Ancestral Swiftness"))
                                    && Me.HealthPercent < ShamanSettings.SelfAncestralSwiftnessHeal
                                    && Me.PredictedHealthPercent(includeMyHeals: true) < ShamanSettings.SelfAncestralSwiftnessHeal,
                                new PrioritySelector(
                                    Spell.HandleOffGCD( Spell.BuffSelf("Ancestral Swiftness") ),
                                    new PrioritySelector(
                                        new Sequence(
                                            ctx => (float)Me.HealthPercent,
                                            new Action(r => Logger.WriteDebug("Healing Surge: {0:F1}% Predict:{1:F1}% and moving:{2}, cancast:{3}", (float)r, Me.PredictedHealthPercent(includeMyHeals: true), Me.IsMoving, Spell.CanCastHack("Healing Surge", Me, skipWowCheck: false))),
                                            Spell.Cast(
                                                "Healing Surge",
                                                mov => true,
                                                on => Me,
                                                req => true,
                                                cancel => Me.HealthPercent > 85
                                                ),
                                            new WaitContinue(TimeSpan.FromMilliseconds(500), until => !Me.IsCasting && Me.HealthPercent > (1.1 * ((float)until)), new ActionAlwaysSucceed()),
                                            new Action(r => Logger.WriteDebug("Healing Surge: After Heal Attempted: {0:F1}% Predicted: {1:F1}%", Me.HealthPercent, Me.PredictedHealthPercent(includeMyHeals: true)))
                                            ),
                                        new Action(r => Logger.WriteDebug("Healing Surge: After Heal Skipped: {0:F1}% Predicted: {1:F1}%", Me.HealthPercent, Me.PredictedHealthPercent(includeMyHeals: true)))
                                        )
                                    )
                                )
                            )
                        ),

                    offheal
                    )
                );
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="gapCloser"></param>
        /// <returns></returns>
        public static Composite CreateRangedHealerMovementBehavior(Composite gapCloser = null)
        {
            int moveNearTank;
            int stopNearTank;

            if (SingularRoutine.CurrentWoWContext != WoWContext.Instances)
            {
                return(new ActionAlwaysFail());
            }

            if (!SingularSettings.Instance.StayNearTank)
            {
                return(Movement.CreateMoveBehindTargetBehavior());
            }

            if (gapCloser == null)
            {
                gapCloser = new ActionAlwaysFail();
            }

            bool incombat = (Dynamics.CompositeBuilder.CurrentBehaviorType & BehaviorType.InCombat) != (BehaviorType)0;

            if (incombat)
            {
                moveNearTank = Math.Max(5, SingularSettings.Instance.StayNearTankRangeCombat);
                stopNearTank = Math.Max(moveNearTank / 2, moveNearTank - 5);
            }
            else
            {
                moveNearTank = Math.Max(5, SingularSettings.Instance.StayNearTankRangeRest);
                stopNearTank = Math.Max(moveNearTank / 2, moveNearTank - 5);
            }

            return(new PrioritySelector(
                       ctx => (Me.Combat && Me.GotTarget() && Unit.ValidUnit(Me.CurrentTarget))
                    ? Me.CurrentTarget
                    : null,
                       new Decorator(
                           ret => ret != null,
                           new Sequence(
                               new PrioritySelector(
                                   gapCloser,
                                   Movement.CreateMoveToLosBehavior(),
                                   Movement.CreateMoveToUnitBehavior(on => Me.CurrentTarget, 30, 25),
                                   Movement.CreateEnsureMovementStoppedBehavior(25f),
                                   // to account for Mistweaver Monks facing Soothing Mist target automatically
                                   new Decorator(
                                       req => !Spell.IsChannelling(),
                                       Movement.CreateFaceTargetBehavior()
                                       )
                                   ),
                               new ActionAlwaysFail()
                               )
                           ),
                       new Decorator(
                           ret => ret == null,
                           new Sequence(
                               CreateStayNearTankBehavior(gapCloser),
                               new ActionAlwaysFail()
                               )
                           )
                       ));
        }
        /// <summary>
        /// stays within range of Tank as they move.  settings configurable by user.
        /// </summary>
        /// <param name="gapCloser">ability to close distance more quickly than running (such as Roll)</param>
        /// <returns></returns>
        public static Composite CreateStayNearTankBehavior(Composite gapCloser = null)
        {
            int moveNearTank;
            int stopNearTank;

            if (gapCloser == null)
            {
                gapCloser = new ActionAlwaysFail();
            }

            if (!SingularSettings.Instance.StayNearTank)
            {
                return(new ActionAlwaysFail());
            }

            if (SingularRoutine.CurrentWoWContext != WoWContext.Instances)
            {
                return(new ActionAlwaysFail());
            }

            if (IsThisBehaviorCalledDuringCombat())
            {
                moveNearTank = Math.Max(5, SingularSettings.Instance.StayNearTankRangeCombat);
                stopNearTank = (moveNearTank * 7) / 10;
            }
            else
            {
                moveNearTank = Math.Max(5, SingularSettings.Instance.StayNearTankRangeRest);
                stopNearTank = (moveNearTank * 6) / 10;     // be slightly more elastic at rest
            }

            Logger.WriteDebug("StayNearTank in {0}: will move towards at {1} yds and stop if within {2} yds", Dynamics.CompositeBuilder.CurrentBehaviorType, moveNearTank, stopNearTank);
            return(new PrioritySelector(
                       ctx => HealerManager.TankToStayNear,

                       // no healing needed, then move within heal range of tank
                       new ThrottlePasses(
                           1,
                           TimeSpan.FromSeconds(5),
                           RunStatus.Failure,
                           new Action(t => {
                if (SingularSettings.Debug)
                {
                    WoWUnit tankToStayNear = (WoWUnit)t;
                    if (t != null)
                    {
                        ;
                    }
                    else if (!Group.Tanks.Any())
                    {
                        Logger.WriteDiagnostic(Color.HotPink, "TankToStayNear: no group members with Role=Tank");
                    }
                    else
                    {
                        Logger.WriteDebug(Color.HotPink, "TankToStayNear:  {0} tanks in group", Group.Tanks.Count());
                        int i = 0;
                        foreach (var tank in Group.Tanks.OrderByDescending(gt => gt == RaFHelper.Leader).ThenBy(gt => gt.DistanceSqr))
                        {
                            Logger.WriteDebug(Color.HotPink, "TankToStayNear[{0}]: {1} Health={2:F1}%, Dist={3:F1}, TankPt={4}, MePt={5}, TankMov={6}, MeMov={7}, LoS={8}, LoSS={9}, Combat={10}, MeCombat={11}, ",
                                              i++,
                                              tank.SafeName(),
                                              tank.HealthPercent,
                                              tank.SpellDistance(),
                                              tank.Location,
                                              Me.Location,
                                              tank.IsMoving.ToYN(),
                                              Me.IsMoving.ToYN(),
                                              tank.InLineOfSight.ToYN(),
                                              tank.InLineOfSpellSight.ToYN(),
                                              tank.Combat.ToYN(),
                                              Me.Combat.ToYN()
                                              );
                        }

                        Logger.WriteDebug(Color.HotPink, "TankToStayNear: current TargetList has {0} units", Targeting.Instance.TargetList.Count());
                        i = 0;
                        foreach (var target in Targeting.Instance.TargetList)
                        {
                            Logger.WriteDebug(Color.HotPink, "CurrentTargets[{0}]: {1} {2:F1}% @ {3:F1}",
                                              i++,
                                              target.SafeName(),
                                              target.HealthPercent,
                                              target.SpellDistance()
                                              );
                        }
                    }
                }
                return RunStatus.Failure;
            })
                           ),
                       new Decorator(
                           ret => ((WoWUnit)ret) != null,
                           new Sequence(
                               new PrioritySelector(
                                   gapCloser,
                                   Movement.CreateMoveToLosBehavior(unit => ((WoWUnit)unit)),
                                   Movement.CreateMoveToUnitBehavior(unit => ((WoWUnit)unit), moveNearTank, stopNearTank)
                                   // , Movement.CreateEnsureMovementStoppedBehavior(stopNearTank, unit => (WoWUnit)unit, "in heal range of tank")
                                   ),
                               new ActionAlwaysFail()
                               )
                           )
                       ));
        }
Example #14
0
        /// <summary>
        ///   Creates a move behind target behavior. If it cannot fully navigate will move to target location
        /// </summary>
        /// <remarks>
        ///   Created 2/12/2011.
        /// </remarks>
        /// <param name="requirements">Aditional requirments.</param>
        /// <returns>.</returns>
        public static Composite CreateMoveBehindTargetBehavior(SimpleBooleanDelegate requirements, Composite gapCloser = null)
        {
            if ( !SingularSettings.Instance.MeleeMoveBehind)
                return new ActionAlwaysFail();

            if (requirements == null)
                return new ActionAlwaysFail();

            if (SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds)
                return new ActionAlwaysFail();

            if (gapCloser == null)
                gapCloser = new ActionAlwaysFail();

            return new Decorator(
                ret =>
                {
                    if (MovementManager.IsMovementDisabled
                        || !SingularRoutine.IsAllowed(Styx.CommonBot.Routines.CapabilityFlags.MoveBehind)
                        || !requirements(ret)
                        || Spell.IsCastingOrChannelling()
                        || Group.MeIsTank)
                        return false;
                    var currentTarget = Me.CurrentTarget;
                    if (currentTarget == null || Me.IsBehind(currentTarget) || !currentTarget.IsAlive || BossList.AvoidRearBosses.Contains(currentTarget.Entry))
                        return false;
                    return (currentTarget.Stunned || currentTarget.CurrentTargetGuid != Me.Guid)
                        && requirements(ret);
                },
                new PrioritySelector(
                    ctx => CalculatePointBehindTarget(),
                    new Decorator(
                        req => Navigator.CanNavigateFully(Me.Location, (WoWPoint)req),
                        new Sequence(
                            new Action(ret => Logger.WriteDebug(Color.White, "MoveBehind: behind {0} @ {1:F1} yds", Me.CurrentTarget.SafeName(), Me.CurrentTarget.Distance)),
                            new Action(behindPoint => Navigator.MoveTo((WoWPoint)behindPoint)),
                            new Action(behindPoint => StopMoving.AtLocation((WoWPoint)behindPoint)),
                            new PrioritySelector(
                                new Decorator(
                                    req => MovementManager.IsClassMovementAllowed,
                                    gapCloser
                                    ),
                                new ActionAlwaysSucceed()
                                )
                            )
                        )
                    )
                );
        }
Example #15
0
        public static Composite CreateMonkDpsHealBehavior()
        {
            Composite offheal;

            if (!SingularSettings.Instance.DpsOffHealAllowed)
            {
                offheal = new ActionAlwaysFail();
            }
            else
            {
                offheal = new Decorator(
                    ret => HealerManager.ActingAsOffHealer,
                    CreateMonkOffHealBehavior()
                    );
            }

            return(new Decorator(
                       ret => !Spell.IsGlobalCooldown() && !Spell.IsCastingOrChannelling(),
                       new PrioritySelector(

                           new Decorator(
                               ret => !Me.Combat &&
                               !Me.IsMoving &&
                               Me.HealthPercent <= 85 && // not redundant... this eliminates unnecessary GetPredicted... checks
                               SpellManager.HasSpell("Effuse") &&
                               Me.PredictedHealthPercent(includeMyHeals: true) < 85,
                               new PrioritySelector(
                                   new Sequence(
                                       ctx => (float)Me.HealthPercent,
                                       new Action(r => Logger.WriteDebug("Effuse: {0:F1}% Predict:{1:F1}% and moving:{2}, cancast:{3}", (float)r, Me.PredictedHealthPercent(includeMyHeals: true), Me.IsMoving, Spell.CanCastHack("Effuse", Me, skipWowCheck: false))),
                                       Spell.Cast(
                                           "Effuse",
                                           mov => true,
                                           on => Me,
                                           req => true,
                                           cancel => Me.HealthPercent > 85
                                           ),
                                       new WaitContinue(TimeSpan.FromMilliseconds(500), until => !Me.IsCasting && Me.HealthPercent > (1.1 * ((float)until)), new ActionAlwaysSucceed()),
                                       new Action(r => Logger.WriteDebug("Effuse: After Heal Attempted: {0:F1}% Predicted: {1:F1}%", Me.HealthPercent, Me.PredictedHealthPercent(includeMyHeals: true)))
                                       )
                                   )
                               ),

                           new Decorator(
                               ret => Me.Combat,

                               new PrioritySelector(

                                   // add buff / shield here
                                   Spell.HandleOffGCD(
                                       new Throttle(
                                           1,
                                           Spell.BuffSelf("Dampen Harm", req => Me.HealthPercent < MonkSettings.DampenHarmPct || MonkSettings.DampenHarmCount <= Unit.UnfriendlyUnits(40).Count(u => u.IsAlive && u.CurrentTargetGuid == Me.Guid && !u.IsTrivial()))
                                           )
                                       ),

                                   // save myself if possible
                                   new Decorator(
                                       ret => (!Me.IsInGroup() || Battlegrounds.IsInsideBattleground) &&
                                       !Me.IsMoving &&
                                       Me.HealthPercent < MonkSettings.Effuse &&
                                       Me.PredictedHealthPercent(includeMyHeals: true) < MonkSettings.Effuse,
                                       new PrioritySelector(
                                           new Sequence(
                                               ctx => (float)Me.HealthPercent,
                                               new Action(r => Logger.WriteDebug("Effuse: {0:F1}% Predict:{1:F1}% and moving:{2}, cancast:{3}", (float)r, Me.PredictedHealthPercent(includeMyHeals: true), Me.IsMoving, Spell.CanCastHack("Effuse", Me, skipWowCheck: false))),
                                               Spell.Cast(
                                                   "Effuse",
                                                   mov => true,
                                                   on => Me,
                                                   req => true,
                                                   cancel => Me.HealthPercent > 85
                                                   ),
                                               new WaitContinue(TimeSpan.FromMilliseconds(500), until => !Me.IsCasting && Me.HealthPercent > (1.1 * ((float)until)), new ActionAlwaysSucceed()),
                                               new Action(r => Logger.WriteDebug("Effuse: After Heal Attempted: {0:F1}% Predicted: {1:F1}%", Me.HealthPercent, Me.PredictedHealthPercent(includeMyHeals: true)))
                                               ),
                                           new Action(r => Logger.WriteDebug("Effuse: After Heal Skipped: {0:F1}% Predicted: {1:F1}%", Me.HealthPercent, Me.PredictedHealthPercent(includeMyHeals: true)))
                                           )
                                       )
                                   )
                               ),

                           offheal
                           )
                       ));
        }