Jump to content

Switch target, polymorph, switch to original target


Recommended Posts

Right, so based on my previous experience with moving after frostnova I now know tha for something like this I will need FightEvents.OnFightLoop.

The idea is simple, however with my limited knowledge of CSharp I was not able to make this work reliably and/or as smooth as I would like to.

The scripting language I'm familiar with is AutoIt and if I was using that I would grab the attacking units, put them into an array, polymorph the second, make the script remember which mob index I polymorphed and go back to any other index. Or grab all the mobs attacking me into an array, polymorph any of them really, probably the one that I have targetted to make it simple, and then do a for loop to loop through them all and check for unit.HaveBuff("Polymorph"), find the first that doesn't have it and attack.

I have tried several versions but I always hit a brick wall due to my CSharp ignorance.

Here's my total pseudo code (don't laugh too hard, or do :-D ) that works but only somewhat. It correctly casts poly, switches target but unfortunately once the target is dead the bot doesn't start fighting with the polymorphed mob. Despite being in combat it considers itself not fighting and tries to move on. The worst scenario is when I have 'Pause if player nearby' selected, once I kill the non-poly mob and player is nearby, it stops, sits there and takes the punishment from the recently unpolymorphed mob.

Spoiler

		var unitsAffectingMyCombat = ObjectManager.GetUnitAttackPlayer();
		if (unitsAffectingMyCombat.Count == 2) // if there are two attackers then do...
		{	
			Logging.Write("START COMBAT ROTATION FIRST IF STATEMENT");
			
			SpellManager.CastSpellByNameLUA("Polymorph"); // cast poly
			
			// wait until one mob has poly and set it as var unitsWithPoly
			var unitsWithPoly = unitsAffectingMyCombat.Where(u => u != null && u.IsValid && u.HaveBuff("Polymorph")).ToList();
			var unitToAttackPoly = unitsWithPoly.OrderBy(unitLow => unitLow.HealthPercent).FirstOrDefault();
			while (unitsWithPoly.Count == 0)
			{
				unitsWithPoly = unitsAffectingMyCombat.Where(u => u != null && u.IsValid && u.HaveBuff("Polymorph")).ToList();
				unitToAttackPoly = unitsWithPoly.OrderBy(unitLow => unitLow.HealthPercent).FirstOrDefault();
				Thread.Sleep(1000);
				Logging.Write("WAITING FOR POLY");
			}
			
			int countWithPoly = unitsWithPoly.Count;
			string countWithPolyString = countWithPoly.ToString();
			Logging.Write("unitsWithPoly = " + countWithPolyString);
			
			// check how many mobs DO NOT have poly and pick one with lower HP
			var unitsAttackMe = unitsAffectingMyCombat.Where(u => u != null && u.IsValid && !u.HaveBuff("Polymorph")).ToList();
			var unitToAttack = unitsAttackMe.OrderBy(unitLow => unitLow.HealthPercent).FirstOrDefault();
			
			int countOrigo = unitsAttackMe.Count;
			string countOrigoString = countOrigo.ToString();
			Logging.Write("unitsAttackMe = " + countOrigoString);
			
			// at this point there is one mob with poly and one mob without poly
			
			FightEvents.OnFightLoop += (unit, cancelable) => {
				Logging.Write("LOOP");
				if (unitToAttack != null && unitToAttack.IsValid && unitToAttack.IsAlive && !unitToAttack.IsMyTarget) // if the mob without poly is not my target, switch to it
				{
					Interact.InteractGameObject(unitToAttack.GetBaseAddress, !ObjectManager.Me.GetMove);
					cancelable.Cancel = true;
				}
				else if ((unitToAttack == null || !unitToAttack.IsValid || !unitToAttack.IsAlive) && unitToAttackPoly != null && unitToAttackPoly.IsValid && unitToAttackPoly.IsAlive  && !unitToAttackPoly.IsMyTarget) // if the mob without poly is dead and the mob with poly is alive start fighting with it.
				{
					Interact.InteractGameObject(unitToAttackPoly.GetBaseAddress, !ObjectManager.Me.GetMove);
					Fight.StartFight(unitToAttackPoly.GetBaseAddress);
					cancelable.Cancel = true;
				}
			};
		}

 

There must be a clean way to do this.

Link to comment
Share on other sites

You don't need to use onfightloop. This could be an if in your rotation.

Once again, I can't test right now but I don't see why the below couldn't do the trick (there are probably cleaner ways to do it):

	List<WoWUnit> attackers = ObjectManager.GetUnitAttackPlayer();
        if (attackers.Count > 1)
        {
            WoWUnit mainTarget = attackers.Where(u => u.HealthPercent == attackers.Min(x => x.HealthPercent)).FirstOrDefault();
            WoWUnit polyTarget = attackers.Where(u => u.HealthPercent == attackers.Max(x => x.HealthPercent)).FirstOrDefault();     
            if (!polyTarget.HaveBuff("Polymorph") && polyTarget != mainTarget)
            {
                Interact.InteractGameObject(polyTarget.GetBaseAddress);
                SpellManager.CastSpellByNameLUA("Polymorph");
                Thread.Sleep(500);
                Interact.InteractGameObject(mainTarget.GetBaseAddress);
            }       
        }

~edit: you'd probably want to add some more conditions to make sure polymorph is known etc.

Link to comment
Share on other sites

That will most likely result in the bot just throwing around random targeting. You absolutely NEED to do this during on the OnFightLoop and cancel all other events.
As for polymorphs not being valid targets - it's because they don't target you. That's why the bot doesn't know it's being attacked. You need to actively start a fight with them again (build this into your fightclass).
I had the same problem in TBC. 

To avoid the bot trying to polymorph another target when pulling more mobs, you can sort them by Guid + HP + Level + Entry, that should result in always the same target being selected.

 

Link to comment
Share on other sites

3 minutes ago, Matenia said:

That will most likely result in the bot just throwing around random targeting. You absolutely NEED to do this during on the OnFightLoop and cancel all other events.

That's true

Link to comment
Share on other sites

45 minutes ago, Matenia said:

That will most likely result in the bot just throwing around random targeting. 

Can you explain to me why that would be? We have a defined poly target and a main target? The only issue I could think of is that the polytarget could change because there could be multiple mobs at 100% hp. You could get around that though with another condition

Link to comment
Share on other sites

Well the way I understand it is that if you don't use OnFightLoop and you call InteractGameObject and it instantly switches back to the original target, the one bot decided to target.

I can polymorph a mob but it will keep it targetted until it dies despite InteractGameObject switching to a different target.

So if the bot targets MOB1 and you InteractGameObject with MOB2 and cast Poly, it might work. But if the bot targets MOB1 and you poly MOB1, it will keep switching between MOB2 and MOB1.

I guess it could work if polytarget was NOT my original target.

Link to comment
Share on other sites

1 hour ago, Matenia said:

You absolutely NEED to do this during on the OnFightLoop and cancel all other events.

How do I cancel all other events?

I tried cancelable.Cancel = true within my OnFightLoop and I'm not 100% convinced it cancelled my main fight loop.

God damn, I'm totally lost :-D

EDIT: the thing is, if we do this (WoWUnit mainTarget = attackers.Where(u => u.HealthPercent == attackers.Min(x => x.HealthPercent)).FirstOrDefault();) inside an OnFightLoop it will "forget" the poly target, won't it? Because it will get redefined everytime it loops...

Link to comment
Share on other sites

1 hour ago, Seminko said:

Well the way I understand it is that if you don't use OnFightLoop and you call InteractGameObject and it instantly switches back to the original target, the one bot decided to target.

I can polymorph a mob but it will keep it targetted until it dies despite InteractGameObject switching to a different target.

So if the bot targets MOB1 and you InteractGameObject with MOB2 and cast Poly, it might work. But if the bot targets MOB1 and you poly MOB1, it will keep switching between MOB2 and MOB1.

I guess it could work if polytarget was NOT my original target.

Pretty much this. Plus, since the bot runs on another thread, sometimes it switches your target back BEFORE your poly cast even starts. Then you end up polying 2 targets back and forth and looking like a retard. Basically why Jasabi could never get polymorph to work correctly, I believe.

cancelable.Cancel = true is correct. However, I recommend NOT breaking the OnFightLoop event handler UNTIL your target has been successfully polymorphed. You can solve this with a while loop (Thread.Sleep inside) until poly is on another target.

Link to comment
Share on other sites

1 hour ago, Matenia said:

cancelable.Cancel = true is correct. However, I recommend NOT breaking the OnFightLoop event handler UNTIL your target has been successfully polymorphed. You can solve this with a while loop (Thread.Sleep inside) until poly is on another target.

Just so I understand... From my testing today I found out that if I have OnFightLoop it combines my BAU rotation with the OnFightLoop rotation. It doesn't really seem it superseeds the BAU rotation. So if I go cancelable.Cancel = true; what will it cancel? The OnFightLoop rotation or everything else except the OnFightLoop rotation?

Can Cancel be used from within the OnFightLoop?

Link to comment
Share on other sites

Sorry, I don't follow. My understanding is the OnFightLoop "loop" is acting like a while loop everytime I'm in combat, doing its thing continuously.

Let's say, if I have the code below and the first argument would be true, cancelable.Cancel would be set to true, what would happen? It would skip the second if argument in the OnFightLoop and start right from the start?

FightEvents.OnFightLoop += (unit, cancelable) => {
	if (ObjectManager.Target.GetDistance <= 8 && (!ObjectManager.Target.HaveBuff("Frost Nova") || !ObjectManager.Target.HaveBuff("Frostbite")) && (_timerFrostNova == null || _timerFrostNova.IsReady) && ObjectManager.Target.HealthPercent >= 20)
	{
		Move.StrafeRight(Move.MoveAction.PressKey, 1250);
		cancelable.Cancel = true;
	}
	
	if (ObjectManager.Target.GetDistance <= 10 && (!ObjectManager.Target.HaveBuff("Frost Nova") || !ObjectManager.Target.HaveBuff("Frostbite")) && (_timerFrostNova == null || _timerFrostNova.IsReady) && ObjectManager.Target.HealthPercent >= 20)
	{
		Move.StrafeLeft(Move.MoveAction.PressKey, 1250);
	}
};

If that is the case can I disable OnFightLoop? I'm looking at the += and thinking if I did -= it would somehow remove things from the OnFightLoop? If so, how?

 

Btw guys, this is great, this is how I like to learn, not sitting in books, that doesn't work for me at all. I have to dive right in and figure things out on real life examples. ;) Thanks for being so helpful!

Link to comment
Share on other sites

8 minutes ago, Seminko said:

Btw guys, this is great, this is how I like to learn, not sitting in books, that doesn't work for me at all. I have to dive right in and figure things out on real life examples. ;) Thanks for being so helpful!

100% agree :happy:

Link to comment
Share on other sites

You are actually subscribing to an event (hence the term event handler).
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/events/how-to-subscribe-to-and-unsubscribe-from-events

So your entire code ALWAYS gets executed - but whatever follows this event (the original fighting code by wRobot) can be stopped by setting cancelable.Cancel to true.

Link to comment
Share on other sites

4 minutes ago, Matenia said:

You are actually subscribing to an event (hence the term event handler).
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/events/how-to-subscribe-to-and-unsubscribe-from-events

So your entire code ALWAYS gets executed - but whatever follows this event (the original fighting code by wRobot) can be stopped by setting cancelable.Cancel to true.

So both the OnFightLoop and my FightClass code will get executed, the only thing we are cancelling is wrobot's "default built in" fighting code / behavior? That's strange, kind of. If that is so, what would happen if I did:

FightEvents.OnFightLoop += (unit, cancelable) => {
	cancelable.Cancel = true;
};

Well, time to experiment...

Link to comment
Share on other sites

Absolutely noob question.

Inside the OnFightLoop I have WoWUnit currentTarget = etc.etc. What the heck do I need to do to be able to use it in an IF statement outside of the OnFightLoop?

I want to test using it in my main rotation "if polyTarget.IsValid" (now gives an error) and if that is true I want to do "Interact.InteractGameObject(polyTarget.GetBaseAddress);" which also gives an error, saying it doesn't exist in the current context.

Do I need to define it at the start? What type do I define it as?

Link to comment
Share on other sites

You need to define the variable outside of the scope of the event handler (onfightloop eventhandler) and then assign it inside the event handler. 
I'm not sure why you need that to achieve it?

Link to comment
Share on other sites

Well no luck, as I was saying the OnFightLoop and my BAU rotation are being combined together. So instead of casting polymorph, it tries to cast Frostbolt all the time... Dang it...

Link to comment
Share on other sites

So I made it work. I basically used a bool inside the OnFightLoop and set it to true when there are more than 1 mob attacking me. While true my BAU combat rotation won't trigger... once the Poly is applied, the bool is set to false and normal combat continues.

To wake the sheep just CastSpellByNameLUA.

Link to comment
Share on other sites

  • 1 year later...
List<WoWUnit> attackers = ObjectManager.GetUnitAttackPlayer();

        FightEvents.OnFightLoop += (unit, cancelable) =>
        {
            if (attackers.Count > 1 && Polymorph.KnownSpell && Polymorph.IsSpellUsable && ObjectManager.Target.GetDistance <= 25 && presence.KnownSpell && presence.IsSpellUsable)
            {
                WoWUnit mainTarget = attackers.Where(u => u.HealthPercent == attackers.Min(x => x.HealthPercent)).FirstOrDefault();
                WoWUnit polyTarget = attackers.Where(u => u.HealthPercent == attackers.Max(x => x.HealthPercent)).FirstOrDefault();
                if (!polyTarget.HaveBuff("Polymorph") && polyTarget != mainTarget)
                {
                    SpellManager.CastSpellByNameOn("Polymorph", polyTarget.Name);
                    Usefuls.WaitIsCasting();
                    return;
                }
            }
        };

(non tested)

Link to comment
Share on other sites

  • 10 months later...

Hello, for any1 who needs, I have implemented working polymorph offtarget with attacking poly after mainTarget is dead. Its quite reliable unsless there are 2 polyTargets - 2 mobs with same name and level pulled after mainTarget. If you guys could make a workaround, please share, but I will deffinitelly try to overcome it too (mby just run from them is a solution).

        List<WoWUnit> attackers = ObjectManager.GetUnitAttackPlayer();
        if (attackers.Count >= 2 && PolyMorph.KnownSpell)
        {
            FightEvents.OnFightLoop += (unit, cancelable) =>
            {
                WoWUnit mainTarget = attackers.Where(u => u.HealthPercent == attackers.Min(x => x.HealthPercent)).FirstOrDefault();
                WoWUnit polyTarget = attackers.Where(u => u.HealthPercent == attackers.Max(x => x.HealthPercent)).FirstOrDefault();
                if (!polyTarget.HaveBuff("Polymorph") && polyTarget != mainTarget && !ObjectManager.Me.IsDeadMe && !mainTarget.IsDead && !polyTarget.IsDead)
                {
                    Interact.InteractGameObject(polyTarget.GetBaseAddress);
                    SpellManager.CastSpellByNameLUA("Polymorph");
                    Usefuls.WaitIsCasting();
                    Interact.InteractGameObject(mainTarget.GetBaseAddress);
                }
                else if (mainTarget.IsDead && !polyTarget.IsDead && !ObjectManager.Me.IsDeadMe)
                {
                    Interact.InteractGameObject(polyTarget.GetBaseAddress);
                    SpellManager.CastSpellByIdLUA(116);
                    Thread.Sleep(500);
                }
            };
            return;
        }

 

Link to comment
Share on other sites

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...