示例#1
0
        /// <summary>
        ///   Creates a move to location behavior. Will return RunStatus.Success if it has reached the location, or stopped in range. Best used at the end of a rotation.
        /// </summary>
        /// <remarks>
        ///   Created 5/1/2011.
        /// </remarks>
        /// <param name = "location">The location.</param>
        /// <param name = "stopInRange">true to stop in range.</param>
        /// <param name = "range">The range.</param>
        /// <returns>.</returns>
        public static Composite CreateMoveToLocationBehavior(LocationRetriever location, bool stopInRange, DynamicRangeRetriever range)
        {
            // Do not f**k with this. It will ensure we stop in range if we're supposed to.
            // Otherwise it'll stick to the targets ass like flies on dog shit.
            // Specifying a range of, 2 or so, will ensure we're constantly running to the target. Specifying 0 will cause us to spin in circles around the target
            // or chase it down like mad. (PVP oriented behavior)
            return
                (new Decorator(

                     // Don't run if the movement is disabled.
                     ret => CLUSettings.Instance.EnableMovement,
                     new PrioritySelector(
                         new Decorator(

                             // Give it a little more than 1/2 a yard buffer to get it right. CTM is never 'exact' on where we land. So don't expect it to be.
                             ret => stopInRange && Me.Location.Distance(location(ret)) < range(ret),
                             new PrioritySelector(
                                 EnsureMovementStoppedBehavior(),

                                 // In short; if we're not moving, just 'succeed' here, so we break the tree.
                                 new Action(ret => RunStatus.Success)
                                 )
                             ),
                         new Action(ret => Navigator.MoveTo(location(ret)))
                         )));
        }
示例#2
0
 public static Composite CreateMoveToMeleeBehavior(LocationRetriever location, bool stopInRange)
 {
     return
         (new Decorator(
              ret => !StyxWoW.Me.IsCasting,
              CreateMoveToLocationBehavior(location, stopInRange, ret => StyxWoW.Me.CurrentTarget.IsPlayer ? 2f : Spell.MeleeRange)));
 }
示例#3
0
 public Composite CreateMoveToMeleeBehavior(LocationRetriever location, bool stopInRange)
 {
     return
         new Decorator(
             ret => !StyxWoW.Me.IsCasting,
             CreateMoveToLocationBehavior(location, stopInRange, ret => 8f));
 }
示例#4
0
 /// <summary>
 ///  Creates a behavior to cast a pet action by name of the pet spell on specified location, if extra conditions are met
 ///  (like Freeze of Water Elemental)
 /// </summary>
 /// <param name="action"> The name of the pet spell that will be casted. </param>
 /// <param name="location"> The point to click. </param>
 /// <param name="extra"> Extra conditions that will be checked. </param>
 /// <returns></returns>
 public static Composite CreateCastPetActionOnLocation(string action, LocationRetriever location, SimpleBooleanDelegate extra)
 {
     return(new Decorator(
                ret => extra(ret) && PetManager.CanCastPetAction(action),
                new Sequence(
                    new Action(ret => PetManager.CastPetAction(action)),
                    new WaitContinue(System.TimeSpan.FromMilliseconds(250), ret => false, new ActionAlwaysSucceed()),
                    new Action(ret => SpellManager.ClickRemoteLocation(location(ret))))));
 }
示例#5
0
 /// <summary>
 ///  Creates a behavior to cast a pet action by name of the pet spell on specified location, if extra conditions are met
 ///  (like Freeze of Water Elemental)
 /// </summary>
 /// <param name="action"> The name of the pet spell that will be casted. </param>
 /// <param name="location"> The point to click. </param>
 /// <param name="extra"> Extra conditions that will be checked. </param>
 /// <returns></returns>
 public static Composite CreateCastPetActionOnLocation(string action, LocationRetriever location, SimpleBooleanDelegate extra)
 {
     return(new Decorator(
                ret => extra(ret) && PetManager.CanCastPetAction(action),
                new Sequence(
                    new Action(ret => PetManager.CastPetAction(action)),
                    new ActionSleep(250),
                    new Action(ret => LegacySpellManager.ClickRemoteLocation(location(ret))))));
 }
示例#6
0
        public void should_return_correct_location_by_location_id()
        {
            ILocationMaster location = LocationRetriever.GetLocationByLocationId(_locationId2);

            Assert.IsTrue(location.IsAvAvailable);
            Assert.IsTrue(location.IsPhoneAvailable);
            Assert.IsTrue(location.IsVideoConfAvailable);
            Assert.AreEqual("Building2", location.LocationBuilding);
            Assert.AreEqual(10, location.LocationCapacity);
            Assert.AreEqual("1", location.LocationFloor);
            Assert.AreEqual(_locationId2, location.LocationId);
            Assert.AreEqual("Test 2", location.LocationName);
            Assert.AreEqual("TestRoom", location.LocationRoom);
        }
示例#7
0
 /// <summary>
 ///   Creates a behavior to cast a spell by name, on the ground at the specified location. Returns RunStatus.Success if successful, RunStatus.Failure otherwise.
 /// </summary>
 /// <remarks>
 ///   Created 5/2/2011.
 /// </remarks>
 /// <param name = "spell">The spell.</param>
 /// <param name = "onLocation">The on location.</param>
 /// <param name = "requirements">The requirements.</param>
 /// <returns>.</returns>
 public static Composite CastOnGround(string spell, LocationRetriever onLocation, SimpleBooleanDelegate requirements)
 {
     return(new Decorator(
                ret =>
                requirements(ret) && onLocation != null && SpellManager.CanCast(spell) &&
                (StyxWoW.Me.Location.Distance(onLocation(ret)) <= SpellManager.Spells[spell].MaxRange || SpellManager.Spells[spell].MaxRange == 0),
                new Sequence(
                    new Action(ret => Logger.Write("Casting {0} at location {1}", spell, onLocation(ret))),
                    new Action(ret => SpellManager.Cast(spell)),
                    new WaitContinue(
                        1,
                        ret => StyxWoW.Me.CurrentPendingCursorSpell != null &&
                        StyxWoW.Me.CurrentPendingCursorSpell.Name == spell,
                        new ActionAlwaysSucceed()),
                    new Action(ret => LegacySpellManager.ClickRemoteLocation(onLocation(ret))))
                ));
 }
示例#8
0
 public Composite CreateMoveToLocationBehavior(LocationRetriever location, bool stopInRange, DynamicRangeRetriever range)
 {
     // Do not f**k with this. It will ensure we stop in range if we're supposed to.
     // Otherwise it'll stick to the targets ass like flies on dog shit.
     // Specifying a range of, 2 or so, will ensure we're constantly running to the target. Specifying 0 will cause us to spin in circles around the target
     // or chase it down like mad. (PVP oriented behavior)
     return new PrioritySelector(
         new Decorator(ret => stopInRange && StyxWoW.Me.CharmedUnit.Location.Distance(location(ret)) < range(ret),
             CreateEnsureMovementStoppedBehavior()),
         new Decorator(ret => StyxWoW.Me.CharmedUnit.Location.Distance(location(ret)) > range(ret),
             new Action(ret =>
             {
                 Navigator.MoveTo(location(ret));
                 return RunStatus.Failure;
             }))
         );
 }
示例#9
0
 /// <summary>
 ///   Creates a behavior to cast a spell by name, on the ground at the specified location. Returns
 ///   RunStatus.Success if successful, RunStatus.Failure otherwise.
 /// </summary>
 /// <remarks>
 ///   Created 5/2/2011.
 /// </remarks>
 /// <param name = "spell">The spell.</param>
 /// <param name = "onLocation">The on location.</param>
 /// <returns>.</returns>
 public static Composite CastOnGround(string spell, LocationRetriever onLocation)
 {
     return(CastOnGround(spell, onLocation, ret => true));
 }
示例#10
0
 public static Composite CreateMoveToMeleeBehavior(LocationRetriever location, bool stopInRange)
 {
     return
         new Decorator(
             ret => !StyxWoW.Me.IsCasting,
             CreateMoveToLocationBehavior(location, stopInRange, ret => StyxWoW.Me.CurrentTarget.IsPlayer ? 2f : Spell.MeleeRange));
 }
示例#11
0
 /// <summary>
 ///   Creates a move to location behavior. Will return RunStatus.Success if it has reached the location, or stopped in range. Best used at the end of a rotation.
 /// </summary>
 /// <remarks>
 ///   Created 5/1/2011.
 /// </remarks>
 /// <param name = "location">The location.</param>
 /// <param name = "stopInRange">true to stop in range.</param>
 /// <param name = "range">The range.</param>
 /// <returns>.</returns>
 public static Composite CreateMoveToLocationBehavior(LocationRetriever location, bool stopInRange, DynamicRangeRetriever range)
 {
     // Do not f**k with this. It will ensure we stop in range if we're supposed to.
     // Otherwise it'll stick to the targets ass like flies on dog shit.
     // Specifying a range of, 2 or so, will ensure we're constantly running to the target. Specifying 0 will cause us to spin in circles around the target
     // or chase it down like mad. (PVP oriented behavior)
     return
         new Decorator(
             // Don't run if the movement is disabled.
             ret => !SingularSettings.Instance.DisableAllMovement,
             new PrioritySelector(
                 new Decorator(
                     // Give it a little more than 1/2 a yard buffer to get it right. CTM is never 'exact' on where we land. So don't expect it to be.
                     ret => stopInRange && StyxWoW.Me.Location.Distance(location(ret)) < range(ret),
                     new PrioritySelector(
                         CreateEnsureMovementStoppedBehavior(),
                         // In short; if we're not moving, just 'succeed' here, so we break the tree.
                         new Action(ret => RunStatus.Success)
                         )
                     ),
                 new Action(ret => Navigator.MoveTo(location(ret)))
                 ));
 }
示例#12
0
        /// <summary>
        ///   Creates a behavior to cast a spell by name, on the ground at the specified location. Returns RunStatus.Success if successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/2/2011.
        /// </remarks>
        /// <param name = "spell">The spell.</param>
        /// <param name = "onLocation">The on location.</param>
        /// <param name = "requirements">The requirements.</param>
        /// <param name="waitForSpell">Waits for spell to become active on cursor if true. </param>
        /// <returns>.</returns>
        public static Composite CastOnGround(string spell, LocationRetriever onLocation,
            SimpleBooleanDelegate requirements, bool waitForSpell = true, SimpleStringDelegate targetDesc = null)
        {
            return
                new Decorator(
                    ret => requirements(ret)
                        && onLocation != null
                        && Spell.CanCastHack(spell, null, skipWowCheck:true)
                        && LocationInRange(spell, onLocation(ret))
                        && GameWorld.IsInLineOfSpellSight(StyxWoW.Me.GetTraceLinePos(), onLocation(ret)),
                    new Sequence(
                        new Action(ret => Logger.Write("Casting {0} {1}at location {2} at {3:F1} yds", spell, targetDesc == null ? "" : "on " + targetDesc(ret) + " ", onLocation(ret), onLocation(ret).Distance(StyxWoW.Me.Location))),

                        new Action(ret => { return SpellManager.Cast(spell) ? RunStatus.Success : RunStatus.Failure; } ),

                        new DecoratorContinue(
                            ctx => waitForSpell,
                            new PrioritySelector(
                                new WaitContinue(1,
                                    ret => GetPendingCursorSpell != null && GetPendingCursorSpell.Name == spell,
                                    new ActionAlwaysSucceed()
                                    ),
                                new Action(r =>
                                {
                                    Logger.WriteDebug("error: spell {0} not seen as pending on cursor after 1 second", spell);
                                    return RunStatus.Failure;
                                })
                                )
                            ),

                        new Action(ret => SpellManager.ClickRemoteLocation(onLocation(ret))),

                        // check for we are done status
                        new PrioritySelector(
                // done if cursor doesn't have spell anymore
                            new Decorator(
                                ret => !waitForSpell,
                                new Action(r => Lua.DoString("SpellStopTargeting()"))   //just in case
                                ),

                            new Wait(TimeSpan.FromMilliseconds(750),
                                ret => Spell.GetPendingCursorSpell == null || Me.IsCasting || Me.IsChanneling,
                                new ActionAlwaysSucceed()
                                ),

                            // otherwise cancel
                            new Action(ret =>
                            {
                                Logger.WriteDebug("/cancel {0} - click {1} failed -OR- Pending Cursor Spell API broken -- distance={2:F1} yds, loss={3}, face={4}",
                                    spell,
                                    onLocation(ret),
                                    StyxWoW.Me.Location.Distance(onLocation(ret)),
                                    GameWorld.IsInLineOfSpellSight(StyxWoW.Me.GetTraceLinePos(), onLocation(ret)),
                                    StyxWoW.Me.IsSafelyFacing(onLocation(ret))
                                    );

                                // Pending Spell Cursor API is broken... seems like we can't really check at this point, so assume it failed and worked... uggghhh
                                Lua.DoString("SpellStopTargeting()");
                                return RunStatus.Failure;
                            })
                            )
                        )
                    );
        }
示例#13
0
        public static Composite PreventDoubleCastOnGround(string spell, double expiryTime, LocationRetriever onLocation, CanRunDecoratorDelegate requirements, bool waitForSpell = false)
        {
            return new Decorator(
                    ret =>
                    onLocation != null && requirements(ret) && SpellManager.CanCast(spell) &&
                    /*!BossList.IgnoreAoE.Contains(StyxWoW.Me.CurrentTarget.Entry) &&*/
                    (StyxWoW.Me.Location.Distance(onLocation(ret)) <= SpellManager.Spells[spell].MaxRange ||
                     SpellManager.Spells[spell].MaxRange == 0) && !DoubleCastEntries.ContainsKey(spell.ToString(CultureInfo.InvariantCulture) + onLocation(ret)),
                    new Sequence(
                        new Action(ret => SpellManager.Cast(spell)),

                        new DecoratorContinue(ctx => waitForSpell,
                            new WaitContinue(1,
                                ret =>
                                StyxWoW.Me.CurrentPendingCursorSpell != null &&
                                StyxWoW.Me.CurrentPendingCursorSpell.Name == spell, new ActionAlwaysSucceed())),

                        new Action(ret => SpellManager.ClickRemoteLocation(onLocation(ret))),
                        new Action(ret => CooldownTracker.SpellUsed(spell)),
                        new Action(ret => UpdateDoubleCastEntries(spell.ToString(CultureInfo.InvariantCulture) + onLocation(ret), expiryTime))));
        }
示例#14
0
        /// <summary>
        ///   Creates a behavior to cast a spell by Id, on the ground at the specified location. Returns RunStatus.Success if successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/2/2011.
        /// </remarks>
        /// <param name = "spellid">The spell Id</param>
        /// <param name = "onLocation">The on location.</param>
        /// <param name = "requirements">The requirements.</param>
        /// <param name="waitForSpell">Waits for spell to become active on cursor if true. </param>
        /// <returns>.</returns>
        public static Composite CastOnGround(int spellid, LocationRetriever onLocation,
            CanRunDecoratorDelegate requirements, bool waitForSpell)
        {
            return
                new Decorator(
                    ret =>
                    requirements(ret) && onLocation != null && CLUSettings.Instance.UseAoEAbilities,
                    new Sequence(
                        new Action(ret => CLULogger.Log("Casting {0} at location {1}", spellid, onLocation(ret))),
                        new Action(ret => SpellManager.Cast(spellid)),

                        new DecoratorContinue(ctx => waitForSpell,
                            new WaitContinue(1,
                                ret =>
                                StyxWoW.Me.CurrentPendingCursorSpell != null &&
                                StyxWoW.Me.CurrentPendingCursorSpell.Id == spellid, new ActionAlwaysSucceed())),

                        new Action(ret => SpellManager.ClickRemoteLocation(onLocation(ret)))));
        }
示例#15
0
 /// <summary>
 ///   Creates a behavior to cast a spell by name, on the ground at the specified location. Returns RunStatus.Success if successful, RunStatus.Failure otherwise.
 /// </summary>
 /// <remarks>
 ///   Created 5/2/2011.
 /// </remarks>
 /// <param name = "spell">The spell.</param>
 /// <param name = "onLocation">The on location.</param>
 /// <param name = "requirements">The requirements.</param>
 /// <returns>.</returns>
 public static Composite CastOnGround(string spell, LocationRetriever onLocation,
     SimpleBooleanDelegate requirements)
 {
     return CastOnGround(spell, onLocation, requirements, true);
 }
示例#16
0
 /// <summary>
 ///  Creates a behavior to cast a pet action by name of the pet spell on specified location.  (like Freeze of Water Elemental)
 /// </summary>
 /// <param name="action"> The name of the pet spell that will be casted. </param>
 /// <param name="location"> The point to click. </param>
 /// <returns></returns>
 public static Composite CreateCastPetActionOnLocation(string action, LocationRetriever location)
 {
     return CreateCastPetActionOnLocation(action, location, ret => true);
 }
示例#17
0
 /// <summary>
 ///  Creates a behavior to cast a pet action by name of the pet spell on specified location, if extra conditions are met
 ///  (like Freeze of Water Elemental)
 /// </summary>
 /// <param name="action"> The name of the pet spell that will be casted. </param>
 /// <param name="location"> The point to click. </param>
 /// <param name="extra"> Extra conditions that will be checked. </param>
 /// <returns></returns>
 public static Composite CreateCastPetActionOnLocation(string action, LocationRetriever location, SimpleBooleanDelegate extra)
 {
     return new Decorator(
         ret => extra(ret) && PetManager.CanCastPetAction(action),
         new Sequence(
             new Action(ret => PetManager.CastPetAction(action)),
             new WaitContinue(System.TimeSpan.FromMilliseconds(250), ret => false, new ActionAlwaysSucceed()),
             new Action(ret => SpellManager.ClickRemoteLocation(location(ret)))));
 }
示例#18
0
 /// <summary>
 ///  Creates a behavior to cast a pet action by name of the pet spell on specified location.  (like Freeze of Water Elemental)
 /// </summary>
 /// <param name="action"> The name of the pet spell that will be casted. </param>
 /// <param name="location"> The point to click. </param>
 /// <returns></returns>
 public static Composite CreateCastPetActionOnLocation(string action, LocationRetriever location)
 {
     return(CreateCastPetActionOnLocation(action, location, ret => true));
 }
示例#19
0
 /// <summary>
 ///  Creates a behavior to cast a pet action by name of the pet spell on specified location, if extra conditions are met
 ///  (like Freeze of Water Elemental)
 /// </summary>
 /// <param name="action"> The name of the pet spell that will be casted. </param>
 /// <param name="location"> The point to click. </param>
 /// <param name="extra"> Extra conditions that will be checked. </param>
 /// <returns></returns>
 public static Composite CreateCastPetActionOnLocation(string action, LocationRetriever location, SimpleBooleanDelegate extra)
 {
     return new Decorator(
         ret => extra(ret) && PetManager.CanCastPetAction(action),
         new Sequence(
             new Action(ret => PetManager.CastPetAction(action)),
             new ActionSleep(250),
             new Action(ret => LegacySpellManager.ClickRemoteLocation(location(ret)))));
 }
示例#20
0
        /// <summary>
        ///  Creates a behavior to cast a pet action by name of the pet spell on specified location, if extra conditions are met
        ///  (like Freeze of Water Elemental)
        /// </summary>
        /// <param name="action"> The name of the pet spell that will be casted. </param>
        /// <param name="location"> The point to click. </param>
        /// <param name="extra"> Extra conditions that will be checked. </param>
        /// <returns></returns>
        public static Composite CreateCastPetActionOnLocation(string action, LocationRetriever location, SimpleBooleanDelegate extra)
        {
            return new Decorator(
                ret => StyxWoW.Me.GotAlivePet && extra(ret) && PetManager.CanCastPetAction(action),
                new Sequence(
                    new Action(ret => PetManager.CastPetAction(action)),

                    new WaitContinue(TimeSpan.FromMilliseconds(500),
                        ret => Spell.GetPendingCursorSpell != null, // && Spell.GetPendingOnCursor().Name == spell,
                        new ActionAlwaysSucceed()
                        ),

                    new Action(ret => SpellManager.ClickRemoteLocation(location(ret))),

                    // check for we are done via either success (no spell on cursor) or failure (cursor remains targeting)
                    new PrioritySelector(

                        // wait and if cursor clears, then Success!!!!
                        new Wait(TimeSpan.FromMilliseconds(500),
                            ret => Spell.GetPendingCursorSpell == null,
                            new ActionAlwaysSucceed()
                            ),

                        // otherwise cancel spell and fail ----
                        new Action(ret =>
                        {
                            Logger.Write("pet:/cancel {0} - click {1} failed?  distance={2:F1} yds, loss={3}, face={4}",
                                action,
                                location(ret),
                                StyxWoW.Me.Location.Distance(location(ret)),
                                GameWorld.IsInLineOfSpellSight(StyxWoW.Me.Pet.GetTraceLinePos(), location(ret)),
                                StyxWoW.Me.Pet.IsSafelyFacing(location(ret))
                                );
                            Lua.DoString("SpellStopTargeting()");
                            return RunStatus.Failure;
                        })
                        )
                    )
                );
        }
示例#21
0
 /// <summary>
 ///   Creates a behavior to cast a spell by Id, on the ground at the specified location. Returns RunStatus.Success if successful, RunStatus.Failure otherwise.
 /// </summary>
 /// <remarks>
 ///   Created 5/2/2011.
 /// </remarks>
 /// <param name = "spellid">The spell Id</param>
 /// <param name = "onLocation">The on location.</param>
 /// <param name = "requirements">The requirements.</param>
 /// <returns>.</returns>
 public static Composite CastOnGround(int spellid, LocationRetriever onLocation,
     CanRunDecoratorDelegate requirements)
 {
     return CastOnGround(spellid, onLocation, requirements, true);
 }
示例#22
0
 /// <summary>
 ///   Creates a behavior to cast a spell by name, on the ground at the specified location. Returns
 ///   RunStatus.Success if successful, RunStatus.Failure otherwise.
 /// </summary>
 /// <remarks>
 ///   Created 5/2/2011.
 /// </remarks>
 /// <param name = "spell">The spell.</param>
 /// <param name = "onLocation">The on location.</param>
 /// <returns>.</returns>
 public static Composite CastOnGround(string spell, LocationRetriever onLocation)
 {
     return CastOnGround(spell, onLocation, ret => true);
 }
示例#23
0
        public static Composite CastOnGround(int spellid, LocationRetriever onLocation, CanRunDecoratorDelegate requirements, bool waitForSpell = false)
        {
            return
                new Decorator(
                    ret => onLocation != null && requirements(ret),
                    new Sequence(
                        new Action(ret => SpellManager.Cast(spellid)),

                        new DecoratorContinue(ctx => waitForSpell,
                            new WaitContinue(1,
                                ret =>
                                StyxWoW.Me.CurrentPendingCursorSpell != null &&
                                StyxWoW.Me.CurrentPendingCursorSpell.Id == spellid, new ActionAlwaysSucceed())),

                        new Action(ret => SpellManager.ClickRemoteLocation(onLocation(ret)))));
        }
示例#24
0
        /// <summary>
        ///   Creates a behavior to cast a spell by name, on the ground at the specified location. Returns RunStatus.Success if successful, RunStatus.Failure otherwise.
        /// </summary>
        /// <remarks>
        ///   Created 5/2/2011.
        /// </remarks>
        /// <param name = "spell">The spell.</param>
        /// <param name = "onLocation">The on location.</param>
        /// <param name = "requirements">The requirements.</param>
        /// <param name="waitForSpell">Waits for spell to become active on cursor if true. </param>
        /// <returns>.</returns>
        public static Composite CastOnGround(string spell, LocationRetriever onLocation,
            SimpleBooleanDelegate requirements, bool waitForSpell)
        {
            return
                new Decorator(
                    ret =>
                    requirements(ret) && onLocation != null && SpellManager.CanCast(spell) &&
                    (StyxWoW.Me.Location.Distance(onLocation(ret)) <= SpellManager.Spells[spell].MaxRange ||
                     SpellManager.Spells[spell].MaxRange == 0) && GameWorld.IsInLineOfSpellSight(StyxWoW.Me.Location, onLocation(ret)),
                    new Sequence(
                        new Action(ret => Logger.Write("Casting {0} at location {1}", spell, onLocation(ret))),
                        new Action(ret => SpellManager.Cast(spell)),

                        new DecoratorContinue(ctx => waitForSpell,
                            new WaitContinue(1,
                                ret =>
                                StyxWoW.Me.CurrentPendingCursorSpell != null &&
                                StyxWoW.Me.CurrentPendingCursorSpell.Name == spell, new ActionAlwaysSucceed())),

                        new Action(ret => SpellManager.ClickRemoteLocation(onLocation(ret)))));
        }
示例#25
0
        /*
        public static Composite PreventDoubleHeal(string spell, double expiryTime, int HP = 100, Selection<bool> reqs = null)
        {
            return PreventDoubleHeal(spell, expiryTime, on => HealingManager.HealTarget, HP, reqs);
        }

        public static Composite PreventDoubleHeal(string spell, double expiryTime, UnitSelectionDelegate onUnit, int HP = 100, Selection<bool> reqs = null)
        {
            return new Decorator(
                ret => (onUnit != null && onUnit(ret) != null && (reqs == null || reqs(ret)) && onUnit(ret).HealthPercent <= HP) && !DoubleCastEntries.ContainsKey(spell.ToString(CultureInfo.InvariantCulture) + onUnit(ret).GetHashCode()),
                new Sequence(
                    new Action(a => SpellManager.Cast(spell, onUnit(a))),
                    new Action(ret => CooldownTracker.SpellUsed(spell)),
                    new Action(a => UpdateDoubleCastEntries(spell.ToString(CultureInfo.InvariantCulture) + onUnit(a).GetHashCode(), expiryTime))));
        }
        */
        public static Composite PreventDoubleCastOnGround(string spell, double expiryTime, LocationRetriever onLocation)
        {
            return PreventDoubleCastOnGround(spell, expiryTime, onLocation, ret => true);
        }