Help requested with casting spells from scripts called via MCP

mistahmikey

Active Member
Hi,

I have written a number of scripts that I have attached to MCP buttons that cast spells. Problem is, I am having trouble getting the spells to cast reliably, and after a lot of playing around with it, I am unable to figure out why. So I was hoping someone might be able to shed some light on why this might be.

For fun, I decided to write a script that would manage casting combinations for my monk. Here is the script:

Code:
#include OgrePauseResume.inc

function main()
{
	declare Jab1    string local "Five Rings VI"
	declare Jab2    string local "Silent Palm IV"
	declare Jab3    string local "Waking Dragon VII"
	declare Punch1  string local "Striking Cobra VII"
	declare Punch2  string local "Frozen Palm VII"
	declare Punch3  string local "Arctic Talon V"
	declare Kick1   string local "Rising Dragon VI"
	declare Kick2   string local "Rising Phoenix VII"
	declare Kick3   string local "Roundhouse Kick VII"

	declare MyTargetName string local ""
	declare MyTargetId   int    local 0
	declare MyTargetType string local ""

	declare Jab               string local ""
	declare Punch             string local ""
	declare Kick              string local ""
	declare SpellCount        int    local 0

	echo Executing AutoCombination for ${Me.Name}

	while TRUE
	{
		while !${Target(exists)}
		{
			wait 5
		}

		MyTargetId:Set[${Target.ID}]
		MyTargetName:Set[${Target.Name}]
		MyTargetType:Set[${Target.Type}]

		echo Targetted actor \"${MyTargetName}\" type \"${MyTargetType}\"

		if !${MyTargetType.Equal[NPC]} && !${MyTargetType.Equal[NamedNPC]}
		{
			wait 5

			continue
		}

		echo Now targetting mob \"${MyTargetName}\" 

		while TRUE
		{
			if !${Actor[${MyTargetId}](exists)}
			{
				break
			}

			if ${Me.ToActor.InCombatMode}
			{
				call PauseOgre

				if ${SpellCount} == 0
				{
					if ${Me.Ability[${Jab1}].IsReady} && !${Me.Ability[${Jab1}].IsQueued}
					{
						Jab:Set[${Jab1}]
						SpellCount:Inc
					}
					elseif ${Me.Ability[${Jab2}].IsReady}  && !${Me.Ability[${Jab2}].IsQueued}
					{
						Jab:Set[${Jab2}]
						SpellCount:Inc				
					}
					elseif ${Me.Ability[${Jab3}].IsReady} && !${Me.Ability[${Jab3}].IsQueued}
					{
						Jab:Set[${Jab3}]
						SpellCount:Inc				
					}
				}

				if ${SpellCount} == 1
				{
					if ${Me.Ability[${Punch1}].IsReady} && !${Me.Ability[${Punch1}].IsQueued}
					{
						Punch:Set[${Punch1}]
						SpellCount:Inc
					}
					elseif ${Me.Ability[${Punch2}].IsReady} && !${Me.Ability[${Punch2}].IsQueued}
					{
						Punch:Set[${Punch2}]
						SpellCount:Inc				
					}
					elseif ${Me.Ability[${Punch3}].IsReady} && !${Me.Ability[${Punch3}].IsQueued}
					{
						Punch:Set[${Punch3}]
						SpellCount:Inc				
					}
				}

				if ${SpellCount} == 2
				{
					if ${Me.Ability[${Kick1}].IsReady} && !${Me.Ability[${Kick1}].IsQueued}
					{
						Kick:Set[${Kick1}]
						SpellCount:Inc
					}
					elseif ${Me.Ability[${Kick2}].IsReady} && !${Me.Ability[${Kick2}].IsQueued}
					{
						Kick:Set[${Kick2}]
						SpellCount:Inc				
					}
					elseif ${Me.Ability[${Kick3}].IsReady} && !${Me.Ability[${Kick3}].IsQueued}
					{
						Kick:Set[${Kick3}]
					SpellCount:Inc				
					}
				}

				if ${SpellCount} == 3
				{
					echo Executing Combination \"${Jab}\" \"${Punch}\" \"${Kick}\"
					
					wait 100 !${Me.CastingSpell}

					wait 7

					echo Casting \"${Jab}\" 

					Me.Ability[${Jab}]:Use

					;echo Waiting up to 100 for \"${Jab}\" to not be queued

					wait 100 !${Me.Ability[${Jab}].IsQueued} 

					;echo Waiting up to 100 for \"${Jab}\" to cast

					wait 100 !${Me.CastingSpell}

					;echo Waiting 5 for \"${Jab}\" to recover

					wait 7

					
					echo Casting \"${Punch}\" 

					Me.Ability[${Punch}]:Use

					;echo Waiting up to 100 for \"${Punch}\" to not be queued

					wait 100 !${Me.Ability[${Punch}].IsQueued} 

					;echo Waiting up to 100 for \"${Punch}\" to cast

					wait 100 !${Me.CastingSpell}

					;echo Waiting 5 for \"${Punch}\" to recover

					wait 7


					echo Casting \"${Kick}\" 

					Me.Ability[${Kick}]:Use

					;echo Waiting up to 100 for \"${Kick}\" to not be queued

					wait 100 !${Me.Ability[${Kick}].IsQueued} 

					;echo Waiting up to 100 for \"${Kick}\" to cast

					wait 100 !${Me.CastingSpell}

					;echo Waiting 5 for \"${Kick}\" to recover

					wait 7
				}
				elseif ${SpellCount} > 0 && ${Jab.Equal[${Jab1}]}
				{
					wait 100 !${Me.CastingSpell}

					wait 7
						
					echo Casting \"${Jab}\" when Combination is not ready

					Me.Ability[${Jab}]:Use

					;echo Waiting up to 100 for \"${Jab}\" to not be queued

					wait 100 !${Me.Ability[${Jab}].IsQueued} 

					;echo Waiting up to 100 for \"${Jab}\" to cast

					wait 100 !${Me.CastingSpell}

					;echo Waiting 5 for \"${Jab}\" to recover

					wait 7

				}

				SpellCount:Set[0]

				call ResumeOgre
			}
			
			if !${Target(exists)}
			{
				break
			}

			if ${Target.ID} != ${MyTargetId}
			{
				break
			}

			wait 5
		}
	}

	echo Leaving AutoCombination
}
And here is the include:

Code:
function PauseOgre()
{
	if ${Script[${OgreBotScriptName}](exists)} && !${b_OB_Paused}
	{
		Script[${OgreBotScriptName}]:ExecuteAtom[TogglePause]
	}
}

function ResumeOgre()
{
	if ${Script[${OgreBotScriptName}](exists)} && ${b_OB_Paused}
	{
		Script[${OgreBotScriptName}]:ExecuteAtom[TogglePause]
	}
}
So what I am seeing in this (and other) scripts is that often, even when a spell is ready, it will not cast. Sometimes the spell "dims" just a touch on the hot bar, like it wanted to cast, but then it goes back on. I have tried using many different recovery times (including trying to use the actual ones provided by the Ability type, but I was unable to get values that consistently reflected what they appear to be from examining the spell).

So if someone could take a look at how I am doing the spell casts and advise me about how to do it more reliably I would certainly appreciate it. And believe me, I have spent a LOT of time looking at other code, reading the wikis, and playing around with my code, but I just can't seem to come up with the magic combination.

Thanks very much.
 
Last edited:

mistahmikey

Active Member
Ugh, not sure what happened to all the formatting for this post - it looked fine when I reviewed it. Sorry that the code is not idented properly. If there is some better way to paste code into posts such that it retains its formatting, let me know.
 

bjcasey

ISX Specialist
Ugh, not sure what happened to all the formatting for this post - it looked fine when I reviewed it. Sorry that the code is not idented properly. If there is some better way to paste code into posts such that it retains its formatting, let me know.
Use the code tags. Looks like the number sign.
 

macker0407

Active Member
I thought Ogre allowed you to inject spells into the casting chain? If it does, you'd be better off using Kannkor's spell casting code, rather than your own, because he's probably solved most of the problems already.

But, if you still want to DIY this yourself and/or Ogre doesn't allow arbitrary ability injections, you may want to consider doing the following:

1) Don't use ability names, use their ID's instead. Using the ability names involves ISXEQ2's internal ability cache which introduces latency and has a habit of corrupting itself every now and then
2) .CastingSpell and .IsQueued aren't, in my experience, all that accurate, especially with fast casting CA's. You might want to consider using ${Me.GetGameData[Spells.Casting].Percent} and ${Me.GetGameData[Spells.Casting].ShortLabel} instead to figure out if you're casting a spell or not
3) Think about what occurs when a spell casts from the game client perspective. You already have some fudge values in there for dealing with latencies but they could do with some work. Also, depending on how far down the rabbit hole you feel like going, you can use more accurate values by basing the fudge factor on current latency(${Me.GetGameData[General.Ping].ShortLabel}).
4) When dealing with fast casting CA's, you often can only detect they've been cast after they've, err, been cast. This is usually due to the speed of the spell cast and the latency of the connection itself, along with the fact that certain client data is asynchronous to server actions. Depending on the cast and the recovery times involved, it's sometimes close to impossible to verify the spell has actually been cast without introducing delays into the casting chain that just hurts DPS. Depending on the situation it's sometimes better to simply use a precomputed wait time for the abilities and then blindly queue them up and hope for the best.

Hopefully that helps.
 

pr517

Active Member
The way you have it where you :Use the ability and then wait for it to not be queued (which it already isn't) and wait for you to not be casting (which you probably aren't) will only result in the wait 7 to happen afterwards and there is no guarantee that it will always be exactly 0.7 seconds later that you can do :Use again. Mashing :Use is the problem. You have to wait properly. Here is a really simple snippet that doesn't :Use unless not already casting, waits for the ability to begin casting, waits for the ability to not be casting, handles instant cast abilities, and will at most only wait the correct amount of time even if lag prevents it from knowing if you are really casting or not.

Code:
; (This should be inside some sort of CastSpell function that you call)
if !${Me.CastingSpell}
{
   Me.Ability[xxx]:Use
   if ${Me.Ability[xxx].CastingTime}
   {
      ; Wait for casting
      wait ${Math.Calc64[${Math.Calc[10*${Me.Ability[xxx].CastingTime}+1]}]} ${Me.CastingSpell}

      ; Wait for not casting
      wait ${Math.Calc64[${Math.Calc[10*${Me.Ability[xxx].CastingTime}+1]}]} !${Me.CastingSpell}
      ; or a while-loop while casting to perform other functions like movement, etc.
      ;while ${Me.CastingSpell}
      ;{
      ;   (do something)
      ;   wait 1
      ;}
   }
   else
   {
      wait 2 ${Me.Ability[xxx].TimeUntilReady}
   }
}
 

insanitywiz

Senior Member
The problem you are having is that your script is calling an ability while one is currently being cast, thus queuing it, and then ogre calls an ability, overwriting your queue. Thus the dimming and refreshing thing. Use ogres CastFromUplink and that should resolve that particular issue.

Be aware, there is an EQ2 bug with combinations that makes them rather finicky. Using a combination ability does not start the combo timer if it is already running, so if your normal CA routine includes ANY combo abilities (which it has to if you want to do any sort of DPS) that can make successfully completing a combination a PITA. i.e. Normal CA rotation hits jab, starting the timer, then other stuff happens, then your chain goes off hitting jab and kick, timer ends, then punch fires, and you will not get your combination proc since punch fired after the timer ended.


Edit: Aaaaand, this is me not actually reading the code before responding!
 

pr517

Active Member
My point is that even if Ogre didn't exist he is using :Use too aggressively. Supposedly he is pausing Ogre.
 

mistahmikey

Active Member
Great stuff, thanks so much for all your thoughtful input. Here are some additional information and questions:

1) The things I am generally doing in most of my scripts I don't believe are directly doable from Ogre, as they have to do with casting strings of avoidance/defensive spells, snaps, etc. on demand as the game situation warrants.

2) The script in this post is really an investigative tool. I am trying to see if it improves DPS to maximize combination chances or just take them as they fall serendipitously, so I just run this against the Epic dummy to gather data.

3) I am trying to pause ogre before running my code to avoid conflicts, but I am assuming the pausing action is asynchronous with respect to ogre casting a spell, so when my script runs, it might run my code when ogre is in the middle of casting. If this is not the case (i.e., pausing guarantees ogre has completed its last action, such as a cast, before granting the pause) it would be nice to know.

4) I have never been able to get the CastFromUplink call to actually work. Its been a while since I tried it so I don't remember exactly how I was invoking it, but it would just silently fail. If someone can give me a snippet using this feature that works for them, that would be very helpful. BTW, if I use CastFromUplink, would it no longer be necessary to do the pause/resume thing?

5) One thing I found frustrating was that when I called Me.Ability[xxx].CastingTime/RecoveryTime, the returned values would rarely match what was actually shown when you Examine the spell in game, and were often quite a bit off. Is there something I must do prior to referencing this feature to ensure it is synced up with the actual game data?

Again, very helpful input. I will be playing around with the various suggestions to see what works best. Thanks again for your time.
 

insanitywiz

Senior Member
4) I have never been able to get the CastFromUplink call to actually work. Its been a while since I tried it so I don't remember exactly how I was invoking it, but it would just silently fail. If someone can give me a snippet using this feature that works for them, that would be very helpful.
Code:
relay all "Script[\${OgreBotScriptName}]:QueueCommand[Call CastFromUplink All Frostwall]"
I assume there is a way to directly call for an ability rather then use CastFromUplink, however I don't know it so this way works for me. You can of course refine your relay's to particular PC's, or IS instances, etc, and refine the cast to specific classes, toonnames, even subclasses or group#. Personally, for this sort of script I use class, since I try to never put toonames in my scripts.

BTW, if I use CastFromUplink, would it no longer be necessary to do the pause/resume thing?
Ogrebot would handle injecting the ability into it's casting for you, it will end up as the next ability cast unless some very specific circumstances hits (such as if you have cancel casting to cure selected).
 

mistahmikey

Active Member
Code:
relay all "Script[\${OgreBotScriptName}]:QueueCommand[Call CastFromUplink All Frostwall]"
I assume there is a way to directly call for an ability rather then use CastFromUplink, however I don't know it so this way works for me. You can of course refine your relay's to particular PC's, or IS instances, etc, and refine the cast to specific classes, toonnames, even subclasses or group#. Personally, for this sort of script I use class, since I try to never put toonames in my scripts.

Ogrebot would handle injecting the ability into it's casting for you, it will end up as the next ability cast unless some very specific circumstances hits (such as if you have cancel casting to cure selected).
Ah, then I am thinking using this feature would not work for my combination script, as the spells must run consecutively for the combo to hit. But it might prove useful in other cases where it does not matter if other spells get interjected by ogre into the sequence I generate from the script.
 

Kannkor

Ogre
Ah, then I am thinking using this feature would not work for my combination script, as the spells must run consecutively for the combo to hit. But it might prove useful in other cases where it does not matter if other spells get interjected by ogre into the sequence I generate from the script.
If you're just doing it for combinations, you should use 3 chain tabs. This is actually the EXACT reason I made 3, for monks. Then after testing it, you lost DPS using them. That made me sad.

Having said that, how wouldn't CastFromUplink work for this scenario? Your monk doesn't have a group cure, so if you pass it 3 abilities to cast, it will do them all in the order you send them.

IE:
Script[${OgreBotScriptName}]:QueueCommand[Call CastFromUplink All Jab]
Script[${OgreBotScriptName}]:QueueCommand[Call CastFromUplink All Kick]
Script[${OgreBotScriptName}]:QueueCommand[Call CastFromUplink All Punch]

Of course, you'll have to use the real names. It would then cast them in that order.

As Insanity mentioned, him and I debugged the shit out of monks, and his assestment on the combination bug is true. The only way to have successful combinations, is to never cast a Jab, Kick, or Punch unless it's part of a combination. This is where I seen the DPS loss, because combination wasn't able to make up for the damage lost of never casting those abilities except in a chain, since some are 25'ish second reuse, and some are 7 seconds (I think). So you lost 2 or 3 abilities, per combination, per 30 seconds.

None the less, if your testing proves me wrong, let me know :)
 

mistahmikey

Active Member
As I stated earlier, the AutoCombination script is more for fun and investigation than anything else.

That said, there is something I clearly do not understand about how the chain tabs work if Kannkor believes what I am doing in my script is not significantly different than what can be achieved with the 3 available chains.

So, I am assuming if I have 3 chains:

Jab1, Punch1, Kick1

Jab2, Punch2, Kick2

Jab3, Punch3, Kick3

Then when all 3 of the spells in any one of the chains is ready, only then will that specific chain fire. So, I have at most 3 opportunities to cast a combination.

But suppose, due to timing, I have a situation where only Jab1, Punch2, and Kick3 are ready. My script will fire that combination - in fact, it can fire 27 different combinations of jab, punch, and kick whenever they are ready.

Now I realize that spell recast timing will reduce the total that can actually be fired over time to be less than 27, but I am speculating the real number will be significantly higher than 3. The mathematical analysis that would include the spell timing is well beyond by feeble-mindedness, hence I wrote the script to empirically test it out against the Epic dummy. So, over the duration of killing the dummy, I would expect my script to land more combinations than would happen using the 3 chain tabs. Of course, I also understand there is a DPS downside to dedicating the 9 spells involved to only be used in combinations. I am merely trying to identify and quantify what that downside might be. But I am unable do this yet because I can't get my script to fire the spells reliably; getting some help to fix this why I made this post.

In any case, based on my understanding of how the existing chain tabs work, it seems I would need 27 chains to cover what my script does. Not true?
 

Kannkor

Ogre
As I stated earlier, the AutoCombination script is more for fun and investigation than anything else.

That said, there is something I clearly do not understand about how the chain tabs work if Kannkor believes what I am doing in my script is not significantly different than what can be achieved with the 3 available chains.

So, I am assuming if I have 3 chains:

Jab1, Punch1, Kick1

Jab2, Punch2, Kick2

Jab3, Punch3, Kick3

Then when all 3 of the spells in any one of the chains is ready, only then will that specific chain fire. So, I have at most 3 opportunities to cast a combination.

But suppose, due to timing, I have a situation where only Jab1, Punch2, and Kick3 are ready. My script will fire that combination - in fact, it can fire 27 different combinations of jab, punch, and kick whenever they are ready.

Now I realize that spell recast timing will reduce the total that can actually be fired over time to be less than 27, but I am speculating the real number will be significantly higher than 3. The mathematical analysis that would include the spell timing is well beyond by feeble-mindedness, hence I wrote the script to empirically test it out against the Epic dummy. So, over the duration of killing the dummy, I would expect my script to land more combinations than would happen using the 3 chain tabs. Of course, I also understand there is a DPS downside to dedicating the 9 spells involved to only be used in combinations. I am merely trying to identify and quantify what that downside might be. But I am unable do this yet because I can't get my script to fire the spells reliably; getting some help to fix this why I made this post.

In any case, based on my understanding of how the existing chain tabs work, it seems I would need 27 chains to cover what my script does. Not true?
Arg... (mostly because this eq2-bug really pisses me off more than anything).

Okay, here's the information I have:
Punches - Reuse time:
Five Rings - 16.2s
Artic Talon - 23.7s
Waking Dragon - 5.4s

Kicks
Roundhouse Kick 7.9s
Rising Phoenix 23.7s
Rising Dragon 23.7s

Jabs
Frozen Palm - 23.7s
Silent Palm - 16.2s
Striking Cobra - 23.7s

It's possible I have a few of them in the wrong spots, and this is with my reuse.
So, you can 3 combinations per "round".
1 every 16.2s
1 every 23.7s
1 every 23.7s
You can't speed this up (other than reuse, which would still serve the same purpose).

Now you have 2 choices, to place those with each of the other abilities and don't share any (such as Jab1+Kick1+Punch1), so they can all fire right after each other, OR, just use the ones with the lowest cool downs (in this case, punch3, kick1 +Jab1-3).
Now, the latter option, isn't really an option at all, because of the in-game bug, if you allow Jabs, Kicks, or Punches to fire outside of the chain, it WILL fuck up your combination.
So you really have 1 option if you wish to maximize combination. Put them into 3 "sets" (use some common sense based on reuse), and don't use those abilities anywhere else (such as CA/Named CA). Doing so, you will maximize your combination, and it will hit every single one 100% of the time (okay, not 100% of the time, cause if you eat a knockback in the middle of one, it obviously won't fire, I'm talking training dummy). OR, you fuck the chain tabs, and just let em fire in your CA tab. You can be stragetic in your CA tab, such as place them with a kick/jab/punch/kick/jab/punch, so anytime you start a fight with everything avialable, you'll fire off 3 combinations, then after that, it's up to lady luck.

If you (or anyone else), can figure out a better idea on how to deal with combinations, I'm all ears, I have a monk as my main so... :)

For those who don't understand the combination bug...
There is a "combination" timer. If the timer isn't running when you use a kick, jab, or punch, it starts a timer (2s? 3s?)
If a kick, jab, and punch aren't all used within that timeframe, it fails and clears out the timer. Then it starts again.

How does this fuck it up?
You cast a kick, then an unrelated ability, so you'r down to 1s left on the "timer", now you start your combination chain. You cast a jab (timer expires), you cast a kick (new timer starts), you cast a punch.
Your chain is now done, but you're missing the jab. Unless you have a random CA that fires off now that is a jab, you just lost your combination chain.
This is why we say, if you want to maximize your combinations, you need to not use any of these abilities in the CA/Named CA tab.

Ultimately, could I track every single jab/kick/punch, and force delays to the combination chain based on this? The answer is, fuck you! Err... I mean, yes I could, then introduce more problems with delaying the chains even longer, or trying to prevent all these abilities based on chain reuse etc etc. This is really not worth the time invested.
 

mistahmikey

Active Member
Ok, after looking at the spell timings more closely, all the punches have the same recast and recovery times - for me, the are at 23.4 and .45. For some reason, there are no recovery time listed for these spells, so I assume that means its zero. All the other jab and punch spell timings are less than or equal to this, so it looks like on average you can do about 3 combinations every 23.85 seconds.

So I now have my script's casting working better, and I ran some tests against the dummy.

1) Using just my script (i.e, nothing else being done but combinations and melee) and dedicating the 9 spells to just doing combinations, according to ACT I got off 36 combinations in 5:13. Based on the timing I stated above, I would have expected about 39.

2) Using just the 3 chain tabs, and the same conditions as with my script, I got off 35 combinations in 4:56. Expectation over that period is 37. So pretty much the same situation.

3) Repeating 2 but adding in all the combination spells to the CA tab below the chain entries drove the combination total down to 6! But, interestingly, the total DPS was virtually identical.

This analysis is very rudimentary and quite unscientific, but it does seem to validate what Kannkor said - there ain't no point in trying to maximize combos at the expense of not using the combat arts. Not that he needed validation, of course :)
 

Kannkor

Ogre
Ok, after looking at the spell timings more closely, all the punches have the same recast and recovery times - for me, the are at 23.4 and .45. For some reason, there are no recovery time listed for these spells, so I assume that means its zero. All the other jab and punch spell timings are less than or equal to this, so it looks like on average you can do about 3 combinations every 23.85 seconds.

So I now have my script's casting working better, and I ran some tests against the dummy.

1) Using just my script (i.e, nothing else being done but combinations and melee) and dedicating the 9 spells to just doing combinations, according to ACT I got off 36 combinations in 5:13. Based on the timing I stated above, I would have expected about 39.

2) Using just the 3 chain tabs, and the same conditions as with my script, I got off 35 combinations in 4:56. Expectation over that period is 37. So pretty much the same situation.

3) Repeating 2 but adding in all the combination spells to the CA tab below the chain entries drove the combination total down to 6! But, interestingly, the total DPS was virtually identical.

This analysis is very rudimentary and quite unscientific, but it does seem to validate what Kannkor said - there ain't no point in trying to maximize combos at the expense of not using the combat arts. Not that he needed validation, of course :)
Yea, that makes sense. I thought all of 1 set had the exact same reuse.

Until they fix (what I call a bug), combination is "lets see how lucky one can get".
 
Top Bottom