Apexx 60 Posted July 30, 2017 Share Posted July 30, 2017 Original Thread Topic: I did not want to necro an old thread, but I have included a timer into the ability Holy Light from Paladin spellbook. However, I do not think that it works too well when you are being attacked while healing and it bumps your cast time backwards. If I pulled 3 mobs and wanted to check my fight class to heal at or below 50% health, and I am still being attacked by the mobs, my cast bar gets knocked backwards which increases the cast time. Is there any way to counter this issue? Thanks! Link to comment Share on other sites More sharing options...
reapler 154 Posted July 30, 2017 Share Posted July 30, 2017 Hello, you can use the "ObjectManager" and filter it with linq. I guess it will be used often, so I've made a method for it: /// <summary> /// Get attacking units from another unit /// </summary> /// <param name="from">Take hostile units from this unit</param> /// <param name="maxRange">Take hostile units within this range</param> /// <returns></returns> public List<WoWUnit> AttackingUnits(WoWUnit from, float maxRange = 10) { return ObjectManager.GetWoWUnitHostile().Where(i => from.Guid == i.TargetObject.Guid && from.Position.DistanceTo(i.Position) <= maxRange ).ToList(); } and use it in another method: if (condition1 && ... && AttackingUnits(ObjectManager.Me, 15).Count < 3 ) { //cast spell } Avvi 1 Link to comment Share on other sites More sharing options...
Apexx 60 Posted July 30, 2017 Author Share Posted July 30, 2017 Thanks Reapler, I already have functions to check hostile units in range, stunned units within range, etc. The issue is why does WRobot cast the ability twice in a row even though my health percent is above my threshold? if (player.HealthPercent <= 50 && HolyLightTimer.IsReady) { UseSpell(HolyLight, true, false, 30); HolyLightTimer = new Timer(3000); // Holy Light cast time = 2.5 seconds } The problem is, when casting and you take a hit, your cast bar subtracts value thus the timer is rendered useless. Let's say I pulled 3 hostile units and my health dropped to 48%. I will start to cast Holy Light as intended. However, the tigers are still attacking me during my heal. If my heal was almost done at 90%, the hostile unit attack from one unit will knock the 90% back down to say 85%. Then if I get hit by the second hostile unit, it will bump down from 85%, to 80%. The 3000 millisecond timer most likely is ready at this point, thus I cast Holy Light a second time, even though my Health percent = 90%. Link to comment Share on other sites More sharing options...
reapler 154 Posted July 30, 2017 Share Posted July 30, 2017 I think you need to provide me the header of your "UseSpell" method before i can give a well answer. it could be look like this: public void UseSpell(bool stopMove, bool waitIsCast = true, bool ignoreIfCast = false) Link to comment Share on other sites More sharing options...
Apexx 60 Posted July 30, 2017 Author Share Posted July 30, 2017 protected void UseSpell(Spell spell, bool stopMoving, bool faceTarget = false, float rangeCheck = 5) Link to comment Share on other sites More sharing options...
reapler 154 Posted July 30, 2017 Share Posted July 30, 2017 So how your "new Spell("").Launch();" call look like in your "UseSpell"? Link to comment Share on other sites More sharing options...
Apexx 60 Posted July 31, 2017 Author Share Posted July 31, 2017 protected void UseSpell(Spell spell, bool stopMoving, bool faceTarget = false, float rangeCheck = 5) { if ( !player.IsMounted && spell.KnownSpell && spell.IsSpellUsable && spell.IsDistanceGood && // Not mounted | Spell Known/Usable/Good Distance player.TargetObject.IsAlive && player.TargetObject.IsAttackable && // Target is alive | attackable target.GetDistance <= rangeCheck // Target is within given range ) { if (faceTarget) { MovementManager.Face(player.TargetObject); } spell.Launch(stopMoving); } } Link to comment Share on other sites More sharing options...
reapler 154 Posted July 31, 2017 Share Posted July 31, 2017 Personally i wouldn't use a timer like this. It's better to bind a timer on "SPELL_CAST_SUCCESS" with events and add delay on it(the purpose was another). To your problem: Your "spell.Launch(stopMoving);" in the method is causing now a own loop, because the parameter "waitIsCast is set to true". This means while you are casting holylight, and you have no conditions to check your current cast, then the next call of "UseSpell()" will be executed. If it's called and the character is still casting holy light, it will wait til the current cast is finished. If it's finished the loop in ".Launch();" will break and execute another "SpellManager.CastSpellByNameLUA(this.NameInGame);" Maybe also add "!ObjectManager.Me.IsCast" otherwise if the character is still casting another unnecessary spell, you may need to add the mentioned timer or reduce tickspeed of your rotation. If you would like to add the timer, feel free to ask. Edit: solution would be "spell.Launch(stopMoving, false);" instead of the other launch call Link to comment Share on other sites More sharing options...
Apexx 60 Posted July 31, 2017 Author Share Posted July 31, 2017 Thanks @reapler. I will try it out. Link to comment Share on other sites More sharing options...
Apexx 60 Posted August 22, 2017 Author Share Posted August 22, 2017 Hey @reapler, How is that you use SPELL_CAST_SUCCESS I would like a more accurate wait time between casts. Thank you! Link to comment Share on other sites More sharing options...
reapler 154 Posted August 22, 2017 Share Posted August 22, 2017 Hello, i've created a small example for this, everything else is explained in the comments: using System; using System.Collections.Generic; using System.Threading; using wManager.Plugin; using wManager.Wow.Class; using wManager.Wow.Enums; using wManager.Wow.Helpers; using wManager.Wow.ObjectManager; using static robotManager.Helpful.Logging; public class Main : IPlugin { #region Variables private bool _isLaunched; private int _msDelay = 200; private DateTime _unlockTime = DateTime.Now; private readonly HashSet<string> _noGcdSpells = new HashSet<string> { "Counterspell", //... //http://wowwiki.wikia.com/wiki/Cooldown => Abilities noted for not affecting nor being affected by the global cooldown: }; #endregion #region Properties public bool GcdActive => DateTime.Now < _unlockTime; #endregion #region WRobot Interface public void Initialize() { var pGuid = ToWoWGuid(ObjectManager.Me.Guid); EventsLuaWithArgs.OnEventsLuaWithArgs += delegate(LuaEventsId id, List<string> args) { if ( id == LuaEventsId.COMBAT_LOG_EVENT_UNFILTERED && args[2] == pGuid && !_noGcdSpells.Contains(args[9]) && ( args[1] == "SPELL_CAST_SUCCESS" || args[1] == "SPELL_HEAL" || args[1] == "SPELL_DAMAGE" ) ) { Write("lock"); _unlockTime = DateTime.Now.AddMilliseconds(_msDelay); } }; _isLaunched = true; Write("Loaded"); while (_isLaunched) { try { if (Conditions.ProductIsStartedNotInPause) { Pulse(); } } catch (Exception e) { WriteError(e.ToString()); } Thread.Sleep(30); } } public void Dispose() { _isLaunched = false; } public void Settings() { } #endregion #region Pulse public void Pulse() { var spell = new Spell("Lesser Heal"); if (!GcdActive && spell.IsSpellUsable && !ObjectManager.Me.IsCast && ObjectManager.Me.TargetObject.HealthPercent <= 90 ) { spell.Launch(false, false); //will cast the heal if "_msDelay" was passed } /* if (spell.IsSpellUsable && !ObjectManager.Me.IsCast && ObjectManager.Me.TargetObject.HealthPercent <= 90) { spell.Launch(false, false); //.IsSpellUsable is true after gcd was passed but the heal itself can delay //and cause double heal cast if no additional delay is added like above //so ObjectManager.Me.TargetObject.HealthPercent <= 90 would be true for a short time }*/ } #endregion #region Methods public string ToWoWGuid(ulong guid) { var wowGuid = ObjectManager.Me.Guid.ToString("x").ToUpper(); var c = 16 - wowGuid.Length; for (var i = 0; i < c; i++) { wowGuid = "0" + wowGuid; } return "0x" + wowGuid; } #endregion } if you are going to lower the tickspeed this will rarer happens, but it can still happen. Instant spells aren't included. Matenia, Findeh and Apexx 3 Link to comment Share on other sites More sharing options...
Apexx 60 Posted August 22, 2017 Author Share Posted August 22, 2017 That's wild! Thank you for the exuberant example. I will need to look this over a few times to try and wrap my head around it. Why is this sort of thing not already implemented into WRobot? Also, you defined _msDelay. Where does this number come from? Thank you again! Link to comment Share on other sites More sharing options...
reapler 154 Posted August 22, 2017 Share Posted August 22, 2017 17 minutes ago, Apexx said: That's wild! Thank you for the exuberant example. I will need to look this over a few times to try and wrap my head around it. Why is this sort of thing not already implemented into WRobot? Also, you defined _msDelay. Where does this number come from? It's the wait time after each cast. You may set this to 50ms - 200ms. But i think 50ms should still be ok on low tick speed. Link to comment Share on other sites More sharing options...
Mykoplazma 24 Posted August 22, 2017 Share Posted August 22, 2017 There is a problem with some spells which are messing up the time - they are appearing in the SPELL_CAST_SUCESS event but they are trinkets, and other buffs from items ( back etc ) Link to comment Share on other sites More sharing options...
Apexx 60 Posted August 22, 2017 Author Share Posted August 22, 2017 #region Properties public bool GcdActive => DateTime.Now < _unlockTime; #endregion Error 1 ; expected Error 2 Invalid token ';' in class, struct, or interface member declaration Error 3 Syntax error, '>' expected Error 4 'Main._unlockTime' is a 'field' but is used like a 'type' Error 5 'System.DateTime.Now' is a 'property' but is used like a 'type' Link to comment Share on other sites More sharing options...
reapler 154 Posted August 22, 2017 Share Posted August 22, 2017 39 minutes ago, Apexx said: #region Properties public bool GcdActive => DateTime.Now < _unlockTime; #endregion Error 1 ; expected Error 2 Invalid token ';' in class, struct, or interface member declaration Error 3 Syntax error, '>' expected Error 4 'Main._unlockTime' is a 'field' but is used like a 'type' Error 5 'System.DateTime.Now' is a 'property' but is used like a 'type' I forgot to mention that i compiled it with a newer C# version in Vs. For example in C#6 you can import static type members into namespace. This version should also work with the older version as ".cs" file: Spoiler using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading; using wManager.Plugin; using wManager.Wow.Class; using wManager.Wow.Enums; using wManager.Wow.Helpers; using wManager.Wow.ObjectManager; using robotManager.Helpful; public class Main : IPlugin { #region Variables private bool _isLaunched; private int _msDelay = 150; private DateTime _unlockTime = DateTime.Now; private readonly HashSet<string> _noGcdSpells = new HashSet<string> { "Counterspell", //... //http://wowwiki.wikia.com/wiki/Cooldown => Abilities noted for not affecting nor being affected by the global cooldown: }; #endregion #region Properties public bool GcdActive { get { return DateTime.Now < _unlockTime; } } #endregion #region WRobot Interface public void Initialize() { var pGuid = ToWoWGuid(ObjectManager.Me.Guid); EventsLuaWithArgs.OnEventsLuaWithArgs += delegate(LuaEventsId id, List<string> args) { if ( id == LuaEventsId.COMBAT_LOG_EVENT_UNFILTERED && args[2] == pGuid && !_noGcdSpells.Contains(args[9]) && ( args[1] == "SPELL_CAST_SUCCESS" || args[1] == "SPELL_HEAL" || args[1] == "SPELL_DAMAGE" ) ) { Logging.Write("lock"); _unlockTime = DateTime.Now.AddMilliseconds(_msDelay); } }; _isLaunched = true; Logging.Write("Loaded"); while (_isLaunched) { try { if (Conditions.ProductIsStartedNotInPause) { Pulse(); } } catch (Exception e) { Logging.WriteError(e.ToString()); } Thread.Sleep(30); } } public void Dispose() { _isLaunched = false; } public void Settings() { } #endregion #region Pulse public void Pulse() { var spell = new Spell("Lesser Heal"); if (!GcdActive && spell.IsSpellUsable && !ObjectManager.Me.IsCast && ObjectManager.Me.TargetObject.HealthPercent <= 95 ) { spell.Launch(false, false); //will cast the heal if "_msDelay" was passed } /* if (spell.IsSpellUsable && !ObjectManager.Me.IsCast && ObjectManager.Me.TargetObject.HealthPercent <= 95) { spell.Launch(false, false); //.IsSpellUsable is true after gcd was passed but the heal itself can delay //and cause double heal cast if no additional delay is added like above //so ObjectManager.Me.TargetObject.HealthPercent <= 90 would be true for a short time }*/ } #endregion #region Methods public string ToWoWGuid(ulong guid) { var wowGuid = ObjectManager.Me.Guid.ToString("x").ToUpper(); var c = 16 - wowGuid.Length; for (var i = 0; i < c; i++) { wowGuid = "0" + wowGuid; } return "0x" + wowGuid; } #endregion } Link to comment Share on other sites More sharing options...
Apexx 60 Posted August 22, 2017 Author Share Posted August 22, 2017 Okay that did the trick! Thank you very much! I was wondering one other thing. The main reason that I wanted a slight wait time between certain spells (Spammable spells mainly IE: Priest Smite) is to check the target unit health percentage between casts to determine other abilities based on the target hp % or even the player's hp %. Is it possible to adjust the msDelay value based on the spell method used? Link to comment Share on other sites More sharing options...
Apexx 60 Posted August 22, 2017 Author Share Posted August 22, 2017 It still is casting Lesser Heal twice even though my heath is >= to the determined value set in my settings. It was working fine with my spell Timer before. Link to comment Share on other sites More sharing options...
Mykoplazma 24 Posted August 23, 2017 Share Posted August 23, 2017 If you creating healing rotation from my own perspective is always better to use lower than < condition and then make a priority with strongest spell on top, the limits with >= are not so good ;) If you don't know how to make simple priority system ask me. Link to comment Share on other sites More sharing options...
Findeh 34 Posted August 28, 2017 Share Posted August 28, 2017 On 22.08.2017 at 8:34 PM, Apexx said: It still is casting Lesser Heal twice even though my heath is >= to the determined value set in my settings. It was working fine with my spell Timer before. The simple conditions like this seems working for me: if (ObjectManager.Me.HealthPercent < 40 && !ObjectManager.Me.IsCast) { Spell.Launch(true, true, false); } UPD: No, i lied, not working, still casting twice Link to comment Share on other sites More sharing options...
Apexx 60 Posted August 31, 2017 Author Share Posted August 31, 2017 Adding a timer to the spell ability is the only way that I have gotten not healing twice in a row to work. Link to comment Share on other sites More sharing options...
reapler 154 Posted August 31, 2017 Share Posted August 31, 2017 (edited) I also noticed that the event with gcd lock sometimes react slow. But this works fine for me: public void Pulse()//pulse every 50ms { if (Target.HealthPercent <= 95) { new Spell("Flash of Light").Launch();//waitIsCast is true => no pulse while casting Logging.Write("cast done\nwait 5 seconds"); Thread.Sleep(5000); Logging.Write("let it pulse again"); } Logging.Write("pulse"); } or if you don't want to block pulse and only want to lock the spell cast: private bool _folCast; public void Pulse() { if (Target.HealthPercent <= 95 && !_folCast) { _folCast = true; Task.Run(delegate { new Spell("Flash of Light").Launch();//waitIsCast is true => the task have to wait while casting Logging.Write("cast done\nset _folCast to false in 5 seconds"); Thread.Sleep(5000); _folCast = false; Logging.Write("_folCast is set to false"); }); } Logging.Write("pulse"); } Of course it needs more checks to the cast, but you get the basic idea. The used initialize method: public void Initialize() { _isLaunched = true; Logging.Write("Loaded"); while (_isLaunched) { try { if (Conditions.ProductIsStartedNotInPause) { Pulse(); } } catch (Exception e) { Logging.WriteError(e.ToString()); } Thread.Sleep(50);//lower values will cause rather double casts } } If you use Task.Run for other stuff, ensure it won't trigger multiple times in a loop. If it's still double cast in your fightclass, you should check your routine flow while running. Edited August 31, 2017 by reapler Link to comment Share on other sites More sharing options...
Apexx 60 Posted September 1, 2017 Author Share Posted September 1, 2017 Timer Declaration public static Timer waitTime = new Timer(); Asynchronous Task UseCombatSpell public static async Task<bool> UseCombatSpell(Spell spell, bool stopMoving, bool faceTarget = false, float rangeCheck = float.MaxValue, int castWait = 0) { // Spell validation if (Methods.IsValid(MyTarget, spell)) { // Launch the spell if (!Me.IsCast && waitTime.IsReady) { // Face the target if (faceTarget) MovementManager.Face(Me.TargetObject); Interact.InteractGameObject(MyTarget.GetBaseAddress, stopMoving); SpellManager.CastSpellByNameLUA(spell.Name); // Wait for cooldown + latency await Task.Delay(SpellManager.GetSpellCooldownTimeLeft(spell.Id) + Usefuls.Latency); // Create a new timer for the desired wait time between casts. waitTime = new Timer(castWait); Methods.LogFight("Wait time = " + waitTime.TimeLeft()); return true; } return false; } return false; } Please note, that I removed the rangeCheck portion of code from the above method.Usage: if (await Abilities.LesserHeal()) return true; // Lesser Heal Methods.IsValid is basically making sure that the player knowns the spell, that the spell is usable and in good distance, that the player is not eating or drinking, or is mounted.. That the target is attackable and alive, in distance, and in line of sight etc..Abilities.Spellname is from a custom class of loading and declaring the class spells. Findeh 1 Link to comment Share on other sites More sharing options...
iMod 99 Posted September 6, 2017 Share Posted September 6, 2017 The easier way public class WoWSpell : Spell { private Timer _timer; #region Constructor /// <summary> /// Creates a new instance of the <see cref="WoWSpell"/> class. /// </summary> /// <param name="spellNameEnglish">The spell name.</param> /// <param name="cooldownTimer">The cooldown time.</param> public WoWSpell(string spellNameEnglish, double cooldownTimer) : base(spellNameEnglish) { // Set timer this._timer = new Timer(cooldownTimer); } #endregion #region Public /// <summary> /// Gets the flag if the timer is ready or not. /// </summary> public bool IsReady { get { return this._timer.IsReady; } } /// <summary> /// Casts the spell if it is ready. /// </summary> public new void Launch() { // Is ready? if (!this.IsReady) { // Return return; } // Call launch base.Launch(); // Reset timer this._timer.Reset(); } #endregion } Just a sample how it could be done. Hope it helps some people. Link to comment Share on other sites More sharing options...
Findeh 34 Posted September 6, 2017 Share Posted September 6, 2017 If i get it right, in your example double cooldownTimer Will be a constant, right? But the problem is, it may be different, it's not a constant for the same spell. Spell may be different level (lvl 1 heal may be 1,5s cast and the same lvl 6 spell may be 3,5s cast). Spell cast time may be buffed by talants, or may be not buffed, depending on character. Cast time may be hited back by incoming damage or kicked. If it was hitted back, delay should be longer, if it was kicked, it should be shorter. In a word, it's complicated. Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now