How to write rotation from scratch

Information about addon, how it works, how to install, etc.
4 posts Page 1 of 1
Kaminaris
Site Admin
Posts: 421
Joined: 25 Jun 2017, 03:45


First let me say this. As long as you have non-zero knowledge of programming, writing rotation is easy.
Even if you never tried lua before. Don't be lazy ass and try it yourself.

There are few classes (ex. rogue assassination) that are exception to this but that is another part of story.

Here is the list of steps to take to write your own rotation to whatever you like.

1. List all of the spells/talents/auras you are going to use.
That is easy part because all you need to do is to either write down spell ids by hand (which is time consuming) in the following manner:
local ANYTHING = {
	SpellName = SpellId;
}
ex.
local FT = {
	ArcaneIntellect      = 1459,
	SummonWaterElemental = 31687,
	LonelyWinter         = 205024,
	MirrorImage          = 55342,
	Frostbolt            = 116,
	Counterspell         = 2139,
	IceLance             = 30455,
	Flurry               = 44614,
	FingersOfFrost       = 44544,
	FreezingRain         = 270233,
	FrozenOrb            = 84714,
	Blizzard             = 190356,
	CometStorm           = 153595,
	IceNova              = 157997,
	Ebonbolt             = 257537,
	BrainFreeze          = 190446,
	Icicles              = 205473,
	GlacialSpike         = 199786,
	RayOfFrost           = 205021,
	ConeOfCold           = 120,
	IcyVeins             = 12472,
	RuneOfPower          = 116011,
	RuneOfPowerAura      = 116014,
	Blink                = 1953,
	IceFloes             = 108839,
	WintersChill         = 228358,
	SplittingIce         = 56377,
};
OR you can use my magic function that extracts spells automatically
You need to use browser with developer tools like chrome, firefox. I will write it using chrome as example.

a) Navigate to icy-veins.com
b) Navigate to class guide you are interested, to "Rotation, Cooldowns, and Abilities" page. ex Fire Mage
c) Open developer tools in browser (F12 in chrome)
d) Open console in developer tools (button Console in chrome)
e) Paste this little script in console:

  1. (function() {
  2.     let slist = {};
  3.     jQuery('div.page_content').find('a.spell').each(function() {
  4.         var name = jQuery(this).text().replace(/ /g, '').replace(/'/g, '').replace(/:/g, '').replace(/-/g, '');
  5.         if (name && name.length) {
  6.             slist[name] = jQuery(this).attr('href').match(/[\d]+$/)[0];
  7.         }
  8.     });
  9.  
  10.     var sout = 'local S = {\n';
  11.     for (var i in slist) {
  12.  
  13.         if (slist.hasOwnProperty(i)) {
  14.             sout += '\t' + i + ' = ' + slist[i] + ",\n";
  15.         }
  16.     }
  17.     sout += '};';
  18.     console.log(sout);
  19. })();

f) Execute it and voilà! You have a list of spells for specific Class/Specialization.


Image


Save it somewhere, you will need it later.

2. Write down rotation in simple priority list
There are 2 things you need to know and keep in mind while working on rotation:
  • Rotation is a priority list that is executed in 0.15s intervals (by default).
    This means if guide says something like: Use spell X then use spell Y; lua ain't gonna understand that shit.
  • Rotation function should return spell what needs to be casted after current spell.
    This is the biggest difference between MaxDps rotation functions and Simcraft action priority lists.
    And this is why variable timeShift exists (it's a fucking important variable).
Most (but not all) of icy veins rotations are compatible with MaxDps.
Lets take a look at Frost Mage (I have simplified it a bit,
since you don't care about cooldowns and generally at spells with longer than 60s cooldown):

1.  Ice Lance
Conditions:
a) Player aura  Fingers of Frost has reached 3 stacks

2.  Frost Bomb
Conditions:
a) It is talented (always present for spells from talents)
b) You are currently not casting frost bomb (this is always present in spells that are not instant.)
c) Player aura  Fingers of Frost has at least 1 stack

3.  Freeze from water elemental
Conditions:
a) if it will hit at least 2 adds that can be rooted (bosses are immune to Freeze).
<- You skip spells with conditions like this because it can't be detected.

4. This is a part where it gets fun, icy-veins says something like this:
Cast Frostbolt and immediately Flurry if Brain Freeze is active.
You can't do like this. But you can't forget about it. Solution is pretty simple.
You just need to watch for Brain Freeze aura and cast Flurry BEFORE Frostbolt. So in the end it looks like this:

4 (again).  Flurry
Conditions:
a) Player Aura  Brain Freeze is active
Simple isn't it?

5.  Ice Lance immediately after  Flurry.
Again, not possible to do, what you CAN do is to simply watch for target debuff  Winter's Chill

5 (again).  Ice Lance
Conditions:
a) Target has  Winter's Chill debuff
OR
b) Your current cast is  Flurry
(It really doesn't work for instant casts, BUT if someone wants to cast it without
 Brain Freeze then it will not be instant cast)

There are other conditions and spells here but i'm gonna skip them since it does not really matter for guide purpose.

6. FILLER SPELL, you always want to return some spell id in case of rotations that DOES have filler spell. In case of fire mage it is  Frostbolt
And it has NO conditions.

3. Convert it to rotation function
I will write this as if you would make a Custom Rotation
(its not much different from class module rotations, the only difference is that spell variables are not
inside functions but at the top of the file):

So you start with empty function like this:
  1. function()
  2.     local fd = MaxDps.FrameData;
  3. end

MaxDps.FrameData is a collection of ongoing combat data such as:
  • timeShift (when the next action will be available, rarely used in new format)
  • currentSpell (Id of spell that player is casting at the moment, instant spells are not included)
  • gcdRemains (remaining GCD amount in seconds)
  • gcd (player total GCD at the moment)
  • cooldown (player spell cooldowns and other info about it)
  • buff (player buffs)
  • debuff (target debuffs)
  • talents (player talents, to check if player selected talent use fd.talents[TalentSpellId])
  • azerite (player azerite traits, returns count of traits, if player doesn't have trait, this will return `0`)
  • essences (player selected essences)
  • spellHistory (player last 5 spells including instants, this is an array)
  • timeToDie (time for a current target to die)
First, you add spell list at the beginning so you don't need to put spell ids by hand
(you might want to clean them up a bit and remove spells that you are not gonna use ex. Blink, but I'm not gonna do that in guide):
  1. function()
  2.     local fd = MaxDps.FrameData;
  3.     local FT = {
  4.         ArcaneIntellect      = 1459,
  5.         SummonWaterElemental = 31687,
  6.         LonelyWinter         = 205024,
  7.         MirrorImage          = 55342,
  8.         Frostbolt            = 116,
  9.         Counterspell         = 2139,
  10.         IceLance             = 30455,
  11.         Flurry               = 44614,
  12.         FingersOfFrost       = 44544,
  13.         FreezingRain         = 270233,
  14.         FrozenOrb            = 84714,
  15.         Blizzard             = 190356,
  16.         CometStorm           = 153595,
  17.         IceNova              = 157997,
  18.         Ebonbolt             = 257537,
  19.         BrainFreeze          = 190446,
  20.         Icicles              = 205473,
  21.         GlacialSpike         = 199786,
  22.         RayOfFrost           = 205021,
  23.         ConeOfCold           = 120,
  24.         IcyVeins             = 12472,
  25.         RuneOfPower          = 116011,
  26.         RuneOfPowerAura      = 116014,
  27.         Blink                = 1953,
  28.         IceFloes             = 108839,
  29.         WintersChill         = 228358,
  30.         SplittingIce         = 56377,
  31.     };
  32.  
  33.     -- Your cooldowns goes here
  34.  
  35.     -- Your rotation goes here
  36. end

Start by converting your priority list into lua conditions. They should look like this:
  1. if CONDITIONS then
  2.     return PREFIX.SpellName;
  3. end

So in our case first one is  Ice Lance
Order of conditions is important and should be the same as your priority list.
Look at big point 2 for reference.


1.
  1. if fd.buff[FT.FingersOfFrost].count >= 3 then -- 3 or more charges of fingers of frost
  2.     return FT.IceLance;
  3. end

2.
  1. --Enters (new line chars) does not matter, you can actually write it in one line but I formatted it for readability
  2. if
  3.     fd.talents[_FrostBomb] -- Thats how you check for talents, talent variable is provided in frame data
  4.     and fd.buff[FT.FingersOfFrost].count >= 1 -- checking if at we have at least one stack of fingers of frost
  5.     and fd.currentSpell ~= FT.FrostBomb -- Player is not casting FrostBomb already
  6.     and not fd.debuff[FT.FrostBomb].up -- Target does not already have Frost Bomb debuff
  7. then
  8.     return FT.FrostBomb;
  9. end

3. Skipped


4.
  1. if fd.buff[FT.BrainFreeze].up then
  2.     return FT.Flurry;
  3. end

5.
  1. if
  2.     fd.currentSpell == FT.Flurry -- Player is casting Flurry
  3.     or -- OR
  4.     fd.debuff[FT.WintersChill].up -- Target has Winter's Chill Debuff
  5. then
  6.     return FT.IceLance;
  7. end

6.
  1. return FT.Frostbolt;
Putting it all together:

  1. function()
  2.     local fd = MaxDps.FrameData;
  3.     local FT = {
  4.         ArcaneIntellect      = 1459,
  5.         SummonWaterElemental = 31687,
  6.         LonelyWinter         = 205024,
  7.         MirrorImage          = 55342,
  8.         Frostbolt            = 116,
  9.         Counterspell         = 2139,
  10.         IceLance             = 30455,
  11.         Flurry               = 44614,
  12.         FingersOfFrost       = 44544,
  13.         FreezingRain         = 270233,
  14.         FrozenOrb            = 84714,
  15.         Blizzard             = 190356,
  16.         CometStorm           = 153595,
  17.         IceNova              = 157997,
  18.         Ebonbolt             = 257537,
  19.         BrainFreeze          = 190446,
  20.         Icicles              = 205473,
  21.         GlacialSpike         = 199786,
  22.         RayOfFrost           = 205021,
  23.         ConeOfCold           = 120,
  24.         IcyVeins             = 12472,
  25.         RuneOfPower          = 116011,
  26.         RuneOfPowerAura      = 116014,
  27.         Blink                = 1953,
  28.         IceFloes             = 108839,
  29.         WintersChill         = 228358,
  30.         SplittingIce         = 56377,
  31.     };
  32.  
  33.     -- Your cooldowns goes here
  34.  
  35.     -- Your rotation goes here
  36.     if fd.buff[FT.FingersOfFrost].count >= 3 then -- 3 or more charges of fingers of frost
  37.         return FT.IceLance;
  38.     end
  39.  
  40.     if
  41.         fd.talents[_FrostBomb] -- Thats how you check for talents, talent variable is provided in frame data
  42.         and fd.buff[FT.FingersOfFrost].count >= 1 -- checking if at we have at least one stack of fingers of frost
  43.         and fd.currentSpell ~= FT.FrostBomb -- Player is not casting FrostBomb already
  44.         and not fd.debuff[FT.FrostBomb].up -- Target does not already have Frost Bomb debuff
  45.     then
  46.         return FT.FrostBomb;
  47.     end
  48.  
  49.     if fd.buff[FT.BrainFreeze].up then
  50.         return FT.Flurry;
  51.     end
  52.  
  53.     if
  54.         fd.currentSpell == FT.Flurry -- Player is casting Flurry
  55.         or -- OR
  56.         fd.debuff[FT.WintersChill].up -- Target has Winter's Chill Debuff
  57.     then
  58.         return FT.IceLance;
  59.     end
  60.  
  61.     return FT.Frostbolt;
  62. end

4. Add cooldowns

Below line
-- Your cooldowns goes here

Add your cooldowns, (spells that you skipped but are still a part of your rotation)
General idea is like this:
  1. MaxDps:GlowCooldown(FT.Spell, Condition);

for example Mirror Image:

  1. MaxDps:GlowCooldown(FT.MirrorImage, talents[FT.MirrorImage] and fd.cooldown[MirrorImage].ready);

It will highlight green when Mirror Image is talented and off cooldown.

And that's it. Then you go to training dummy and test your rotation (need to close Custom Rotations window before getting into combat).

For experienced developers it takes around 1h to write rotation from scratch,
you can always use one of the Original rotations and modify it to suit your needs.
lennyrrt
Posts: 1
Joined: 06 Oct 2019, 13:06


Would someone be able to explain, maybe step-by-step, how to get a rotation to work in the add-on after the part of the post that says "First let me say...?"
Benninger
Posts: 1
Joined: 13 Oct 2019, 11:25


The developer script for extracting the spells does not work here (Chrome):

VM15576:6 Uncaught TypeError: Cannot read property '0' of null
at HTMLAnchorElement.<anonymous> (<anonymous>:6:68)
at Function.each (jquery-8101d596b2b8fa35fe3a634ea342d7c3.js:2)
at m.fn.init.each (jquery-8101d596b2b8fa35fe3a634ea342d7c3.js:2)
at <anonymous>:3:48
at <anonymous>:19:3

What am I doing wrong?
Kaminaris
Site Admin
Posts: 421
Joined: 25 Jun 2017, 03:45


This doesn't look like error from script. How did you do it? Have screencast?
4 posts Page 1 of 1
drogie łóżka stolik kawowy stół dębowy rozkładany