Jump to content

Problems with mult-dots


Lockem

Recommended Posts

Having some problems with the below chunk of code... I'm trying to make a dot rotation that will tab between each target and apply my dots (This part works!). My problem is that no matter what I do it seems to want to double cast Immolate... I've tried adding a timer for it, !ObjectManager.Me.IsCast and various sleeps. I'm kind of a C# noob... so excuse any obvious mistakes.

Thanks,

Spoiler

// Pull Rotation...

internal void dotRotation()
    {
        IEnumerable<WoWUnit> unitsToDot = ObjectManager.GetObjectWoWUnit().Where(u => u.IsTargetingMeOrMyPet && (!u.HaveBuff("Corruption") || !u.HaveBuff("Curse of Agony") || !u.HaveBuff("Immolate")));

        foreach (WoWUnit unit in unitsToDot)
        {
            if (ObjectManager.Me.Target != unit.Guid)
            {
                Interact.InteractGameObject(unit.GetBaseAddress);
            }

            if (!ObjectManager.Target.HaveBuff("Corruption"))
            {
                Corruption.Launch();
                Thread.Sleep(Usefuls.Latency + 1200);
                return;
            }
            if (!ObjectManager.Target.HaveBuff("Curse of Agony"))
            {
                CurseofAgony.Launch();
                Thread.Sleep(Usefuls.Latency + 1200);
                return;
            }
            if (!ObjectManager.Target.HaveBuff("Immolate") && !ObjectManager.Me.IsCast)
            {
                Immolate.Launch();
                Thread.Sleep(Usefuls.Latency + 1200);
                return;
            }

            return;
        }

// Rest of rotation once no dot targets are available...

 

 

Link to comment
Share on other sites

Yes, it loops through all targets attacking me or my pet, applies all 3 dots to each of them and then moves on with the rotation. The problem is it always double casts Immolate...

like this:

Corruption > CoA > Immolate x2 > Switch targets > Corruption > CoA > Immolate x2 > Rest of rotation.

Immolate.Launch() is how I'm casting.

Link to comment
Share on other sites

4 minutes ago, Lockem said:

Immolate launch is how I'm casting.

Right...

Hmmm, that's strange. Since you're looping through all the targets and you're not using while but rather if for the spell cast I can't image how it can cast twice.

Basically:

  1. Does it have Corruption
    1. no
    2. cast it
  2. Does it have CoA
    1. no
    2. cast it
  3. Does it have Immolate
    1. no
    2. cast it
  4. Loop for that specific mob ended, whether the immolate has been successfully cast or not
  5. Switching targets

 

BTW, do you use OnFightLoop? Can you share where is dotRotation() being called from? The more code you post the better.

Going to sleep, it's 1AM here :)

Link to comment
Share on other sites

Quote

Basically:

  1. Does it have Corruption
    1. no
    2. cast it
  2. Does it have CoA
    1. no
    2. cast it
  3. Does it have Immolate
    1. no
    2. cast it
  4. Loop for that specific mob ended, whether the immolate has been successfully cast or not
  5. Switching targets

Not exactly...  it will make sure it actually puts immolate up, It's just that it always double casts it even if its on the target. It seems like its looping through dotRotation() while casting the immolate and it thinks it still needs immolate.

Link to comment
Share on other sites

I should have said: "whether the immolate has been successfully APPLIED or not". BTW, how does it make sure it actually puts immolate up? On vanilla where we do SpellManager.CastSpellByNameLUA("Immolate"); it doesn't actually check whether it has been applied. It starts casting Immolate but if I cancel the cast, that's it. I pressume that Spell.Launch(); acts the same way.

This is a longshot but try using List instead of IEnumerable.

List<WoWUnit> unitsToDot =  ObjectManager.GetObjectWoWUnit().Where(u => u.IsTargetingMeOrMyPet && (!u.HaveBuff("Corruption") || !u.HaveBuff("Curse of Agony") || !u.HaveBuff("Immolate")));

Also, don't know how you FC is set up but I pressume you only want dotRotation() to be applied on the mobs you are already in fight with?

EDIT: and again --> Can you share where is dotRotation() being called from and when? The more code you post the better.

Link to comment
Share on other sites

 UPDATE: I'm 95% sure this problem only occurs when the bot is fighting a single mob... I could fix it by doing a count and adding a separate single target dot rotation, but I'd rather not, and I'm curious as to why this is happening.

 

For testing purposes I removed dotRotation() and just made the dots the only thing in my combat rotation. The problem still happens... It seriously seems like the bot is checking for immolate while immolate is being cast and so it "pre-casts" it.

Things I've tried:

  • tweaking latency in General Settings (I normally have 90ms)
  • limiting my framerate to 64fps
  • removing all sleeps
  • adding sleeps
  • adding a timer to immolate 

Where I'm calling CombatRotation()  

Spoiler

internal void Rotation()
    {
        Logging.Write("[My fightclass] Is started.");
        while (_isLaunched)
        {
            try
            {
                if (Conditions.InGameAndConnectedAndAliveAndProductStartedNotInPause && !ObjectManager.Me.IsDeadMe)
                {
                    if (Fight.InFight || Conditions.IsAttackedAndCannotIgnore)
                    {
                        MovementManager.Face(wManager.Wow.ObjectManager.ObjectManager.Target);
                        PetAggro();
                        CombatRotation();
                    }
                    else
                    {
                        if (ObjectManager.Target.Reaction != Reaction.Friendly && ObjectManager.Target.IsAttackable && ObjectManager.Target.GetDistance <= Range)
                        {
                            CombatRotation();
                        }

                        PetManager();
                        BuffRotation();
                        Healthstone();                      
                    }
                }
            }
            catch (Exception e)
            {
                Logging.WriteError("[My fightclass] ERROR: " + e);
            }

            Thread.Sleep(100);
        }
        Logging.Write("[My fightclass] Is now stopped.");
    }

 

My new mult-dot code (I wanted to be able to use .Count) - The Immolate double cast still exists.

Spoiler

   internal void CombatRotation()
    {
        List<WoWUnit> unitsToDot = ObjectManager.GetObjectWoWUnit();

        unitsToDot = unitsToDot.Where(u => u.IsAttackable
                                        && u.GetDistance <= Range
                                        && u.Reaction != Reaction.Friendly
                                        && u.HealthPercent >= 15
                                        && (!u.HaveBuff("Corruption") || !u.HaveBuff("Curse of Agony") || !u.HaveBuff("Immolate")))
                                    .OrderBy(u => u.Guid).ToList();

        foreach (WoWUnit unit in unitsToDot)
        {
            if (ObjectManager.Me.Target != unit.Guid)
            {
                Interact.InteractGameObject(unit.GetBaseAddress);
            }

            if (!ObjectManager.Target.HaveBuff("Corruption"))
            {
                Corruption.Launch();
                Thread.Sleep(Usefuls.Latency + 1200);
                return;
            }
            if (!ObjectManager.Target.HaveBuff("Curse of Agony"))
            {
                CurseofAgony.Launch();
                Thread.Sleep(Usefuls.Latency + 1200);
                return;
            }
            if (!ObjectManager.Target.HaveBuff("Immolate") && !ObjectManager.Me.IsCast)
            {
                Immolate.Launch(true);
                Thread.Sleep(Usefuls.Latency + 1200);
                return;
            }

            return;
        }

        if (DrainSoul.KnownSpell && !ObjectManager.Target.HaveBuff("Drain Soul") &&
            ItemsManager.GetItemCountByNameLUA("Soul Shard") < 5 && ObjectManager.Target.HealthPercent < 15)
        {
            DrainSoul.Launch(true);
        }
        if (DrainLife.KnownSpell && ObjectManager.Me.HealthPercent <= 60)
        {
            DrainLife.Launch(true);
        }
    }

 

 

Link to comment
Share on other sites

Well first thing I would do I set up Logging.Write inside your methods so you know when Immolate is cast, under what conditions etc.

Also, howbout, just for the sake of testing, you did:

if (!ObjectManager.Target.HaveBuff("Immolate"))
{
    Immolate.Launch();
    while (!ObjectManager.Target.HaveBuff("Immolate"))
    {
        Thread.Sleep(50);
    }
    Thread.Sleep(Usefuls.Latency + 1200);
    return;
}

EDIT: since you updated the previous post I'm removing all the stuff that was below... Still am curious what happens if you use the above code

Link to comment
Share on other sites

Quote

if (!ObjectManager.Target.HaveBuff("Immolate"))
{
    Immolate.Launch();
    while (!ObjectManager.Target.HaveBuff("Immolate"))
    {
        Thread.Sleep(50);
    }
    Thread.Sleep(Usefuls.Latency + 1200);
    return;
}

This fixes the problem completely. Weird... thanks!

Still curious why the problem occurred in the first place. Also, my target switching is a little 'jittery'... mind giving me an example of how OnFightLoop would work for target switching?

Link to comment
Share on other sites

So we've gone full circle :).

Basically, the way I understand it, there are FightEvents which handle targetting (and probably more) which you cannot influence or control from within your fightclass - UNLESS when actually using events - OnFightStart, OnFightLoop or OnFightEnd. So when we use OnFightLoop it will loop independently of your default combat roration.

In the OnFightLoop I would only handle targetting and the rest in my combat rotation. It's up to you to come up with the logic.

 

Here's what I think might work - not tested, since I don't have a Lock. This should provide you all of the building blocks to fine tune it to your liking.

Spoiler

public Spell Corruption = new Spell("Corruption");
public Spell CoA = new Spell("Curse of Agony");
public Spell Immolate = new Spell("Immolate");
public Spell DrainSoul = new Spell("Drain Soul");
public Spell DrainLife = new Spell("Drain Life");

public void Initialize() // When product started, initialize and launch Fightclass
{
	_isLaunched = true;
	targetSwitcher(); // calling it here, once called it will register the OnFightLoop and will do that without calling it again (in combat obviously)
	Start();
}

private void targetSwitcher()
{
	FightEvents.OnFightLoop += (unit, cancelable) => {
		WoWUnit targetToSwitchTo = ObjectManager.GetUnitAttackPlayer().Where(u => u.IsValid && u.GetDistance <= Range && u.HealthPercent >= 15 && (!u.HaveBuff("Corruption") || !u.HaveBuff("Curse of Agony") || !u.HaveBuff("Immolate"))).OrderBy(ou => ou.HealthPercent).FirstOrDefault();
		if (targetToSwitchTo.IsValid && ObjectManager.Target.HaveBuff("Corruption") && ObjectManager.Target.HaveBuff("Curse of Agony") && ObjectManager.Target.HaveBuff("Immolate")) // if the target you want to switch to is valid AND your CURRENT target has all of the three buffs, it will switch and stay on the target until it has all of the three buffs, etc etc
		{
			Interact.InteractGameObject(targetToSwitchTo.GetBaseAddress);
		}
	};
}

private void combatRotation()
{
	if (!ObjectManager.Target.HaveBuff("Corruption") && Corruption.IsSpellUsable) // important part that you were missing - IsSpellUsable
	{
		SpellManager.CastSpellByNameLUA("Corruption"); //or Launch, if CastSpellByNameLUA is not usable for TBC
		return;
	}
	else if (!ObjectManager.Target.HaveBuff("Curse of Agony") && CoA.IsSpellUsable)
	{
		SpellManager.CastSpellByNameLUA("Curse of Agony");
		return;
	}
	else if (!ObjectManager.Target.HaveBuff("Immolate") && Immolate.IsSpellUsable)
	{
		SpellManager.CastSpellByNameLUA("Immolate");
		return;
	}
	else if (DrainSoul.KnownSpell && !ObjectManager.Target.HaveBuff("Drain Soul") && ItemsManager.GetItemCountByNameLUA("Soul Shard") < 5 && ObjectManager.Target.HealthPercent < 15  && DrainSoul.IsSpellUsable)
	{
		SpellManager.CastSpellByNameLUA("Drain Soul");
		return;
	}
	else if (DrainLife.KnownSpell && ObjectManager.Me.HealthPercent <= 60  && DrainLife.IsSpellUsable)
	{
		SpellManager.CastSpellByNameLUA("Drain Life");
		return;
	}
	return;
}

private void Start()
{
	while (_isLaunched)
	{
		if (Fight.InFight && ObjectManager.Me.Target > 0 && !Products.InPause && !ObjectManager.Me.IsDeadMe)
		{
			combatRotation();
		}
		Thread.Sleep(50);
	}
}

 

 

Btw, you're posting in the retail version of the forums. There is a dedicated forum for TBC help and support.

Link to comment
Share on other sites

OnFightLoop doesn't actually loop anything. It's a function that is contiously called (like maybe 10 times a second - probably one per new frame, so 60 times per second).
You are registering an event handler that is called from within wRobot code, by another wRobot spawned thread.

So think of it this way:

//wRobot code calls fight stuff here
List<EventHandler> OnFightLoopEventHandlers;
Target(unit); //puts it in ObjectManager.Target, targets it ingame etc
while(true){
	Execute(combatRotation); //for XML fightclasses or something
  	foreach(handler in OnFightLoopEventHandlers){
      	handler.call(unit, cancelable);
    }
  	Thread.Sleep(50);
}
//more wRobot code here

Of course this is pseudo code. But if you call Thread.Sleep and keep the thread locked in a while loop within one of the OnFightLoopEventHandlers, then obviously the bot can't call Target(unit) again (assuming my pseudo code is called again and again as well).
So you're really just blocking default behavior of the bot through event handlers that aren't executed asynchronously.

Link to comment
Share on other sites

Just wanted to give you guys an update...

@Seminko Using the OnFightLoop for targeting worked perfectly!

As for immolate being double cast, it seems to be a pretty common problem with heals and some spells with a cast time... The best solution I could find was adding a timer + delay to Immolate. If you can think of a better way of doing this I would love to know.

Again, thanks for all the help!

Link to comment
Share on other sites

8 hours ago, Lockem said:

Just wanted to give you guys an update...

@Seminko Using the OnFightLoop for targeting worked perfectly!

As for immolate being double cast, it seems to be a pretty common problem with heals and some spells with a cast time... The best solution I could find was adding a timer + delay to Immolate. If you can think of a better way of doing this I would love to know.

Again, thanks for all the help!

Thanks for the response. I just had the same issue on Vanilla recently. Even though I managed to target -> cast -> target last target within a split second, it would sometimes really mess up targeting and just go back and forth too many times.
The way I solved it now is to add a boolean to an OnFightLoop handler that (just for the time I need to target -> target -> target) blocks the OnFightLoop wRobot runs.

Link to comment
Share on other sites

So after some more research / tweaking I'm about 99.9% sure I've fixed my double Immolate problem for good...

private bool ImmoCast;

// Immolate
if (!GcdActive
    && Immolate.KnownSpell
    && Immolate.IsSpellUsable
    && Immolate.IsDistanceGood
    && !ObjectManager.Me.IsCast
    && !ObjectManager.Target.HaveBuff("Immolate"))
    {
    ImmoCast = true;

    if (ImmoCast == true)
    {
    Immolate.Launch(true, false);
    Thread.Sleep(SpellManager.GetSpellCooldownTimeLeft(61304)); <-- This seemed to be the key part to fixing it. I originally had a 3500ms sleep...
    ImmoCast = false;							that obviously caused other problems, this sleep if for the remaining GCD.
    }
}

This post was a good read... specifically the comments by @reapler

 

Link to comment
Share on other sites

I'm not sure with current Wow, because I am just now getting into programming fight classes again, but I remember in the past that "flight times" for spells could be an issue as well as the fact that some spells aren't seen until they tick once. This was an old problem, so it may not be relevant anymore, but something to look at. 

Link to comment
Share on other sites

  • 2 weeks later...

instead of Thread.Sleep(...) try to use wManager.Wow.Helpers.Usefuls.WaitIsCasting()

Sleep rotation for long time is not good, and with this function you alwais can break cast and start another spell.

Link to comment
Share on other sites

My variant (attacks only units in front of player +-90 degrees):

public bool multiDot(string spell,int distance){
  WoWUnit target = ObjectManager.GetWoWUnitHostile()
   .Where(o => o.IsAlive && o.IsValid && !o.HaveBuff(spell) && o.GetDistance <= distance)
   .OrderBy(o => o.Health)
   .FirstOrDefault();
  WoWUnit oldFocus = ObjectManager.Target;
  if (target != null) {
    if (!MovementManager.IsFacing(ObjectManager.Me.Position, ObjectManager.Me.Rotation, target.Position, 1.6f)) return false;
    Interact.InteractGameObject(target.GetBaseAddress, true);
    SpellManager.CastSpellByNameOn(spell, "focus");
    Usefuls.WaitIsCasting();
    Interact.InteractGameObject(oldFocus.GetBaseAddress, true);
    return true;
  }
  return false;
}

 

Link to comment
Share on other sites

  • 1 month later...

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...