Register

Leafkiller's MoP Feral/Guardian Ovale Script

Face-rippin fun.
Revered
Posts: 319
Joined: Tue Feb 08, 2011 5:51 pm

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby ShmooDude » Sat Jun 29, 2013 4:27 pm

Alpheus wrote:If you clipped rip in the first place your new rip would replace both the old rip and the hypothetical new rip but you have no way of knowing if you would have had more or less buffs and damage modifiers up at that time


I think this is the crux of the issue. This is essentially along the same tangent as raffy's post. Currently Ovale can't really "predict" future buffs (or even really the effect of a current buff expiring will have) so all it knows how to do is compare out current bleed to one we could potentially put out at the time.

Not sure there is a "perfect" solution that doesn't directly look at what buffs we have. Here would be a conservative option I think would work well for ovale (of course I have zero proof, lol).

If FB Damage + Old Rip duration damage is less than New Rip over duration of estimated time to next 5 combo points or current rip (whichever is longer).

Since that's a little complicated here's an example.

Old rip is at 8 seconds say does 100k/tick so 400k
FB will average 200k so 600k total for FB

New rip at 130k/tick but it'll take ~10 seconds to get to 5 CP so 650k total for overwriting. If on the other hand it'll only take 6 seconds to get to 5 CP, we use old rip's duration of 8 seconds and its 520k.

Hardest part will be calculating the estimated time to next 5 CP I think.

Exalted
User avatar
Posts: 1201
Joined: Fri Nov 25, 2011 7:49 pm

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby aggixx » Sat Jun 29, 2013 7:42 pm

I had thought about that, but while it would be doable enough in Ovale it would probably way too complicated to do in SimC.
Image

Honored
User avatar
Posts: 72
Joined: Mon Feb 21, 2011 8:56 pm

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby scorpio0920 » Sun Jun 30, 2013 11:06 pm

Sometimes moves will be prompted to use bite, even though it was the case should not bite.

I can not explain exactly when it will happen.

Revered
Posts: 319
Joined: Tue Feb 08, 2011 5:51 pm

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby ShmooDude » Mon Jul 01, 2013 7:51 am

Is there a particular reason the DoC and non-DoC areas of the script are separate? Is it for performance reasons?

So far the only differences I've found are as follows:
DoC specific functionality
Virmin's Bite functionality (which as far as I know doesn't actually do anything at this time)
Execute range Rip logic (which won't function until FB is updated w/ damage values, and I don't see any reason this'd be DoC specific as its logic should apply to non-doc too)
Resource pooling for FB during execute range (not present in non-doc and feels like it should be)
Rip overwrite during RoR (DoC has Rip Ratios required, non-DoC doesn't, any particular reason why?)
Non-Doc FillerConditions has a check for soul of the forest but DoC doesn't (Comparing the two I don't see why its needed?)

All the DoC code could simply be changed to check for the appropriate talent when needed and would make updating something in one not require checking the other. It just seems like there's a lot of updates that happen in one end of the script and not the other and it'd just make things easier overall (it feels like a lot of things get added to the DoC rotation but never added to the non-DoC).

EDIT: Went ahead and added a DoC talent check to all DoC specific functionality and used those functions instead of the non-doc functions and a target dummy test feels like its working as it should (which granted isn't the end all be all test).

Oh, the other thing I was noticing is that your Potion lines in simcraft say to use the potion ~15 seconds before bitw range. Given what we know about the old BitW function, we could probably actually do this within ovale. I think it'd be something like "if target.HealthPercent <=25 -15"? I dunno, might mess around with it a bit.
Last edited by ShmooDude on Mon Jul 01, 2013 8:17 am, edited 1 time in total.

Revered
User avatar
Posts: 323
Joined: Tue Oct 23, 2012 4:19 am

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby Alpheus » Mon Jul 01, 2013 8:14 am

ShmooDude wrote:Is there a particular reason the DoC and non-DoC areas of the script are separate? Is it for performance reasons?

So far the only differences I've found are as follows:
DoC specific functionality
Virmin's Bite functionality (which as far as I know doesn't actually do anything at this time)
Execute range Rip logic (which won't function until FB is updated w/ damage values, and I don't see any reason this'd be DoC specific as its logic should apply to non-doc too)
Resource pooling for FB during execute range (not present in non-doc and feels like it should be)
Rip overwrite during RoR (DoC has Rip Ratios required, non-DoC doesn't, any particular reason why?)
Non-Doc FillerConditions has a check for soul of the forest but DoC doesn't (Comparing the two I don't see why its needed?)

All the DoC code could simply be changed to check for the appropriate talent when needed and would make updating something in one not require checking the other. It just seems like there's a lot of updates that happen in one end of the script and not the other and it'd just make things easier overall (it feels like a lot of things get added to the DoC rotation but never added to the non-DoC).


It's not as simple because back when the action list was being written you not only had extra lines for DoC but also different orders for existing lines which would be a total mess to do with talent conditionals. But I believe the list has been simplified quite a bit and I think raffy even requested an all-in-one action list (check the Catus thread).
Image

Revered
Posts: 319
Joined: Tue Feb 08, 2011 5:51 pm

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby ShmooDude » Mon Jul 01, 2013 8:19 am

Alpheus wrote:It's not as simple because back when the action list was being written you not only had extra lines for DoC but also different orders for existing lines which would be a total mess to do with talent conditionals. But I believe the list has been simplified quite a bit and I think raffy even requested an all-in-one action list (check the Catus thread).


Makes sense and I agree I think its been simplified to the point they can be merged.

Exalted
User avatar
Posts: 1145
Joined: Fri Jun 04, 2010 4:28 am

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby Leafkiller » Mon Jul 01, 2013 2:36 pm

When did this become a discussion about code design? Since I am no longer maintaining the script itself and I am not up to speed on the recent rotation changes, I will only speak in general terms about coding of state machines.

As background, I spent my early years programming on a voice mail system and a PBX (back in the 80s) working on VUI state code, a lot of integrations (protocols), and connection table code that managed the end-to-end connections on a TDM bus. One of the things I noted was there is a tendency when programmers see similar activities to share the state code, particularly in what I "affectionately" like to call instruction pointer state machines (state machines where the state machine aspect has not been explicitly factored out of the code). The reason people like to do this is to try to share code. The problem with this is the potential for side effects. I am going to give two examples from my past experiences to try to illustrate this:

I took over the pbx connection table code from someone who had left. It was known to have a lot of bugs (one way connections for example) and was not structured very well. The first module (file) I picked up was titled something about local connections (the pbx was capable of being networked into four system clusters so connections could have only one part if the connection stayed local, or three parts if it crossed pbx instances - two local and one interconnect). In four different places in this module I found a curious if-else construct that defined three different cases. It turned out that this module was being reused for all three connection types, and this if-else structure was determining which of the three cases called the module so that it could handle the specific case. While the state code looked similar and one might thing reuse was happening, this was a fallacy as what I had was confusing code that was prone to errors/side effects and difficult to maintain. I refactored the code so that the shared parts were pulled out into utility modules, and the state portions were dedicated to each connection type. By doing this I ensured that changes such as how a remote connection was setup, or how the interconnect worked, or how local connections would work would not inadvertently cause bugs when implemented. And I shared all of the code that needed to be shared - by moving it into shared subroutines.

Second example - we had a bug in our VUI where there was an if statement that was covering two possible states that really needed to cover three possible states. This was code that had an extreme amount of state reuse - in others words, you could be hitting it from several different cases, and where you hit it from mattered. I had the engineer working on the fix put together a test matrix to cover all of the possible states we could be in so we could test the fix. It turned out that in order to test a 3 line fix in the code there were 169 different tests. When you state mixing state from multiple paths in order to share code you have this wonderful multiplicative effect. Needless to say, this code was almost impossible to maintain because it shared so much state code it became fragile as any change to fix it for one case could easily break other cases.

As a general principle, I believe in state separation with shared subroutines, libraries or whatever you want to call them.

When we were first working on DoC, HotW and NV, while also taking into account SotF, Incarnation and FoN, I made some separations in the code due to the cross product of the two meaningful talent tiers - which potentially result in 9 rotations. I did not separate it into 9 cases because some were trivially different, but there was enough difference that the code quickly became unmanageable. The very act of adding the same if/else test in multiple places is a sign that state is being shared and that code is becoming fragile. An example of how I combatted that was by moving the code that decided what filler to use into a subroutine so that I could make a single call to execute the filler in all of the states.

With the ongoing changes that blizzard is making, coupled with the constant refinements to the rotation that are being figured out by the community (you), the Ovale script is constantly being refactored. That said, as a general rule, try to refactor it so that state is explicit and not shared, and things that should be shared are factored out into subroutines. I am not close enough to the current version of the code to comment on what it currently looks like or how it should be factored, but I am sure there is a way to make it understandable and supportable without making it fragile along the way. For example, we had many cases early on before I separated DoC and HotW where changes to DoC broke HotW.

Revered
Posts: 319
Joined: Tue Feb 08, 2011 5:51 pm

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby ShmooDude » Mon Jul 01, 2013 4:01 pm

Honestly, because only the DoC half of the script seems to be getting most of the attention. There's a lot of logic there that should be in both DoC and HotW but isn't. I have no problem as a programmer leaving them separate, but updates that apply to both need to actually get applied to both. That's why I suggested merging them, at least for the moment.

Even with me just messing around with the code, I will a LOT of the time put a line in the wrong place and wonder why the heck its not working.

Perhaps then the answer is more subroutines and not less, but a lot of these amount to no more than one line, and would make the code more nightmarish to read. There may not really be a good solution.

Exalted
User avatar
Posts: 1145
Joined: Fri Jun 04, 2010 4:28 am

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby Leafkiller » Tue Jul 02, 2013 12:36 am

Maybe the HotW and DoC scripts can be factored to a much higher level and most everything else put into shared routines. I did something like that when I was working with Mew scripts. It is a question of analyzing the similarities and differences. There is no perfect answer or absolute rules, only general guidelines and enough sense (perhaps aesthetic sense) to know when things should be collapsed together vesus when they should remain separate.

Exalted
User avatar
Posts: 1145
Joined: Fri Jun 04, 2010 4:28 am

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby Leafkiller » Tue Jul 02, 2013 4:43 am

One approach might be to invert the script structure. Build a single common script that calls a set of well defined DoC and HotW subroutines at specific points where different logic is needed.

Revered
Posts: 319
Joined: Tue Feb 08, 2011 5:51 pm

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby ShmooDude » Tue Jul 02, 2013 4:09 pm

Leafkiller wrote:One approach might be to invert the script structure. Build a single common script that calls a set of well defined DoC and HotW subroutines at specific points where different logic is needed.


That kinda feels like where we are now, except not having the DoC specific stuff in subroutines but I won't know for sure till I hear back for aggixx on my questions.

I've been doing a LOT of tinkering over the weekend and added some nice stuff to the script. Some of the stuff:
Factoring in crit (which I had posted before, though now I've added floors and ceilings since the game will let you go to a >100% crit chance. Mostly relevant for Primordius)
Factoring in armor (which was needed for the FB damage below, I scaled it linearly down from 93 to 88 and then ignore it otherwise for the moment, 93 should be correct, 90 is close and honestly don't know on the rest; anyone got a reference page for the armor value of non-boss mobs?)
Ferocious Bite Damage (which allows the execute range code to work I hope, haven't really gotten a chance to test it yet)
Checking if you will be able to use Rip/Rake before Rune expires (ie if you're only gonna have 20 energy before the mastery buff expires, don't bother telling me to rip/rake specifically for Rune; also checks the spell cooldown which should only be the global cooldown except when the cd is artificially increased like in HoF)
If new rip * remaining duration > (old rip * remaining duration + 50 energy FB) then use rip instead of FB (I would think this should always be a DPS buff and at least on the target dummy would pretty much never fire unless I put up a low CP rip simply to test the functionality; will help catch 1 cp rip errors due to 2pt15)
Merged everything under the DoC code and added appropriate talent tests for DoC where needed.

Things I'm thinking of doing:
Similar check for TF like the Rune check above (if I'm not gonna have energy, don't tell me to do something relating to TF; though this one will be much more complicated I think)
Rentaki's soul charm code (rip priority towards the last like 6-8 seconds should probably be high just like rune)
AoE Rotation box

If any of that sounds like stuff you'd want to add to the official script aggixx, let me know and i can either post it here or PM it to you.

Exalted
User avatar
Posts: 861
Joined: Tue Oct 23, 2012 7:15 am

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby raffy » Tue Jul 02, 2013 7:50 pm

On the topic of action/priority lists, I always felt that there must be a better model than just a list of actions. The action list to me is like the lowest-level implementation of a strategy, like an assembly language. There must exist some higher-level abstractions that make both improvement and management of our "rotation" easier and simpler. This type of strategy would need to be "compiled" down to a traditional action list, which would probably end up being significantly more complicated and entwined than one a human could write manually. Sadly, I don't know what this model is, but it's something that's always been at the back of my mind.

Additionally, an interesting experiment would be to parse combat logs into a sequence of abilities, similar to the sequence strings simc generates: "AaaabCadaefafeghahakea..." except it would need to include procs, like Rune, Dancing Steel, etc. From these strings, you could probably learn all kinds of stuff. I'm kind of curious how similar those strings are between different fights or sims.

I wonder if you could automatically generate Ovale-like recommendations purely from a Markov chain-style analysis of these sequence strings. ie. Just record the last N attacks/procs, and lookup a recommendation in a giant table with the highest probability (possibly BitW would cause some confusion.)

Revered
Posts: 319
Joined: Tue Feb 08, 2011 5:51 pm

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby ShmooDude » Tue Jul 02, 2013 7:56 pm

raffy wrote:On the topic of action/priority lists, I always felt that there must be a better model than just a list of actions. The action list to me is like the lowest-level implementation of a strategy, like an assembly language. There must exist some higher-level abstractions that make both improvement and management of our "rotation" easier and simpler. This type of strategy would need to be "compiled" down to a traditional action list, which would probably end up being significantly more complicated and entwined than one a human could write manually. Sadly, I don't know what this model is, but it's something that's always been at the back of my mind.

Additionally, an interesting experiment would be to parse combat logs into a sequence of abilities, similar to the sequence strings simc generates: "AaaabCadaefafeghahakea..." except it would need to include procs, like Rune, Dancing Steel, etc. From these strings, you could probably learn all kinds of stuff. I'm kind of curious how similar those strings are between different fights or sims.

I wonder if you could automatically generate Ovale-like recommendations purely from a Markov chain-style analysis of these sequence strings. ie. Just record the last N attacks/procs, and lookup a recommendation in a giant table with the highest probability (possibly BitW would cause some confusion.)


I get what you're saying for sure, however, programming like that is way above my head (I have a 2 year degree and no real work experience at the moment).

Exalted
User avatar
Posts: 1201
Joined: Fri Nov 25, 2011 7:49 pm

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby aggixx » Tue Jul 02, 2013 9:57 pm

ShmooDude wrote:That kinda feels like where we are now, except not having the DoC specific stuff in subroutines but I won't know for sure till I hear back for aggixx on my questions.

Pretty much what you guessed, no real reason other than it's sometimes hard to apply the changes to both parts. Or in some cases they weren't a gain in SimC for HotW.

ShmooDude wrote:That kinda feels like where we are now, except not having the DoC specific stuff in subroutines but I won't know for sure till I hear back for aggixx on my questions.

I've been doing a LOT of tinkering over the weekend and added some nice stuff to the script. Some of the stuff:
Factoring in crit (which I had posted before, though now I've added floors and ceilings since the game will let you go to a >100% crit chance. Mostly relevant for Primordius)
Factoring in armor (which was needed for the FB damage below, I scaled it linearly down from 93 to 88 and then ignore it otherwise for the moment, 93 should be correct, 90 is close and honestly don't know on the rest; anyone got a reference page for the armor value of non-boss mobs?)
Ferocious Bite Damage (which allows the execute range code to work I hope, haven't really gotten a chance to test it yet)
Checking if you will be able to use Rip/Rake before Rune expires (ie if you're only gonna have 20 energy before the mastery buff expires, don't bother telling me to rip/rake specifically for Rune; also checks the spell cooldown which should only be the global cooldown except when the cd is artificially increased like in HoF)
If new rip * remaining duration > (old rip * remaining duration + 50 energy FB) then use rip instead of FB (I would think this should always be a DPS buff and at least on the target dummy would pretty much never fire unless I put up a low CP rip simply to test the functionality; will help catch 1 cp rip errors due to 2pt15)
Merged everything under the DoC code and added appropriate talent tests for DoC where needed.

Things I'm thinking of doing:
Similar check for TF like the Rune check above (if I'm not gonna have energy, don't tell me to do something relating to TF; though this one will be much more complicated I think)
Rentaki's soul charm code (rip priority towards the last like 6-8 seconds should probably be high just like rune)
AoE Rotation box

If any of that sounds like stuff you'd want to add to the official script aggixx, let me know and i can either post it here or PM it to you.

I'd be interested in those things for the most part yes. I'm concerned about your Rip clip logic, I wouldn't put that in without testing it in SimC first, it's not nearly as straightforward as you might think.
Image

Revered
Posts: 319
Joined: Tue Feb 08, 2011 5:51 pm

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby ShmooDude » Tue Jul 02, 2013 11:10 pm

aggixx wrote:I'd be interested in those things for the most part yes. I'm concerned about your Rip clip logic, I wouldn't put that in without testing it in SimC first, it's not nearly as straightforward as you might think.


Honestly, I bet it wouldn't even trigger in SimCraft. I have yet to see it trigger in 3 hours of raiding tonight. Mostly I put it there as a fail safe to catch when I lag and put up 1 CP rips because of the tier bonus. Its only one line so it'd be easy for you to take out anyhow. On the plus side, the execute range rip logic does seem to work. I'll clean it up a little then either post it here or pm it later tonight.

Spoiler: show
Code: Select all
# Leafkiller's Feral/Guardian druid script.
# Support/Discussion thread: http://fluiddruid.net/forum/viewtopic.php?f=3&t=857
# Cat Rotation based on Simulationcraft single target default script. Contributions to that have come from many ferals.
# Guardian rotation based on the guide at theincbear.com
# Lots of input and constructs from jlam aka Nerien
# Currently maintained by aggixx and Tinderhoof
# Revision History
# 5.3.1 06/24/2013 Update RoR item list to include TF versions, fix Rip not clipping during Rune.
# 5.2.4 04/18/2013 Better RoR code, better rake code, some DPS oriented changes for Guardian while not tanking
# 5.2.3 03/18/2013 Use Mangle to generate combo points except for high energy situations. Fix issue with misplaced NSs.
# 5.2.2 03/09/2013 Smarter FB logic, fix Faerie Fire, fix mastery assumption for RoR, better Thrash logic, better precombat SR logic
# 5.2.1 02/25/2013 Support for 5.2 changes, rough support for Rune of Reorigination
# 5.1.8 02/16/2013 Fix TF not displaying with berserk checked and TF displaying while Berserk is active, fix lookahead issue with Ravage.
# 5.1.7 02/12/2013 Fix FF option, fix WEAKENED_ARMOR.
# 5.1.6 02/10/2013 Fix frontal attack and talent conditional in main button (dream_of_cenarius_talent should be DREAM_OF_CENARIUS_TALENT)
# 5.1.5 02/09/2013 Update to SimC (Base code from Nerien), code consistency and formatting
# 5.1.4 12/07/2012 Tooth and Claw
# 5.1.3 12/07/2012 Tweak for Bear display with alternate setup selected
# 5.1.2 12/07/2012 Misc changes including SR, Ravage!, alternate layout for Aggixx and some script reconciliation
# 5.1.1 11/30/2012 Ravage fix for PVP 4 set and support for faster combo points on crits
# 5.05.14 11/09/2012 New spell ID for clearcasting
# 5.05.14 11/09/2012 Only suggest Feral_spirit if the symbiosis buff is present - otherwise it suggests symbiosis even when the buff is not up
# 5.05.13 10/23/2012 Don't pool during NV
# 5.05.12 10/22/2012 Fix bad spell name in Rake conditional (TIGERS_FURY should be TIGERSFURY)
# 5.05.11 10/22/2012 Fix for energy pooling for non-DoC specs. Small updates based on most recent sim script for TTD. FFF() added. TTD updated.
# 5.05.10 10/21/2012 4 Piece PvP gear support and Nature's Vigil HT code
# 5.05.9 10/16/2012 Feral Spirit support
# 5.05.8 10/14/2012 Fix 3 second SR to interfere less with Rip for HotW (and NV). 60 seconds for Rake.
# 5.05.7 10/14/2012 Update to latest version of sim script - small changes and Rake improvements
# 5.05.6 10/11/2012 Heart of the Wild support, add out of combat support
# 5.05.6 10/10/2012 Update to limit SR using comb points when DoC is up
# 5.05.5a 10/08/2012 Update to include optimizations in simc script
# 5.05.5 09/24/2012 Multiply damage ratios by 100 to avoid rounding issues, put in variable Rip overwriting during BitW, tweak numbers slightly (TF is 14% not 15% for example).
# 5.05.4 09/23/2012 Level 90 script - First complete version
# 5.05.2 09/20/2012 Level 90 script - WiP
# 5.05.1 09/08/2012 First version in Nerien's addon

############################
## Define Spells, Buffs, Items, Talents ##
############################

# Shared spells
Define(BARKSKIN 22812)
    SpellInfo(BARKSKIN cd=60)
Define(FERAL_SPIRIT 110807)
Define(HEALING_TOUCH 5185)
Define(MARK_OF_THE_WILD 1126)
    SpellInfo(MARK_OF_THE_WILD duration=3600)
    SpellAddBuff(MARK_OF_THE_WILD MARK_OF_THE_WILD=1)
Define(SYMBIOSIS 110309)
    SpellAddBuff(SYMBIOSIS SYMBIOSIS=1)
Define(WRATH 5176)

# Shared buffs
Define(DREAM_OF_CENARIUS_DAMAGE 108381)
    SpellInfo(DREAM_OF_CENARIUS_DAMAGE duration=30 )
    SpellAddBuff(DREAM_OF_CENARIUS_DAMAGE DREAM_OF_CENARIUS_DAMAGE=1)
Define(NATURES_VIGIL 124974)
    SpellInfo(NATURES_VIGIL cd=180)
    SpellAddBuff(NATURES_VIGIL NATURES_VIGIL=1)
Define(PREDATORY_SWIFTNESS 69369)
    SpellAddBuff(PREDATORY_SWIFTNESS PREDATORY_SWIFTNESS=1)
Define(PRIMAL_FURY 16961)
Define(TRICKS 57933)
    SpellAddBuff(TRICKS TRICKS=1)
Define(WEAKENED_ARMOR 113746)
    SpellInfo(WEAKENED_ARMOR duration=30)
Define(WEAKENED_BLOWS 115798)
    SpellInfo(WEAKENED_BLOWS duration=30)
Define(CLEARCASTING 135700)
Define(ROR_CRIT 139117)
    SpellInfo(ROR_CRIT duration=10)
Define(ROR_MASTERY 139120)
    SpellInfo(ROR_MASTERY duration=10)
Define(ROR_HASTE 139121)
    SpellInfo(ROR_HASTE duration=10)
    SpellList(ROR ROR_CRIT ROR_MASTERY ROR_HASTE)

# Shared items
ItemList(ROR_ITEM 94532 95802 96174 96546 96918)

# Talents
Define(NATURES_SWIFTNESS_TALENT 4)
Define(RENEWAL_TALENT 5)
Define(CENARION_WARD_TALENT 6)
Define(FAERIE_SWARM_TALENT 7)
Define(TYPHOON_TALENT 9)
Define(SOUL_OF_THE_FOREST 10)
Define(INCARNATION_TALENT 11)
Define(FORCE_OF_NATURE_TALENT 12)
Define(MIGHTY_BASH_TALENT 15)
Define(HEART_OF_THE_WILD_TALENT 16)
Define(DREAM_OF_CENARIUS_TALENT 17)
Define(NATURES_VIGIL_TALENT 18)

# Talent spells
Define(CENARION_WARD 102351)
    SpellInfo(CENARION_WARD cd=30)
Define(FORCE_OF_NATURE 106737)
    SpellInfo(FORCE_OF_NATURE duration=15 cd=60)
Define(HEART_OF_THE_WILD 108292)
    SpellInfo(HEART_OF_THE_WILD cd=360)
    SpellAddBuff(HEART_OF_THE_WILD HEART_OF_THE_WILD=1)
Define(INCARNATION 106731)
    SpellInfo(INCARNATION cd=180)
Define(MIGHTY_BASH 5211)
    SpellInfo(MIGHTY_BASH cd=50)
Define(NATURES_SWIFTNESS 132158)
    SpellInfo(NATURES_SWIFTNESS cd=60)
    SpellAddBuff(NATURES_SWIFTNESS NATURES_SWIFTNESS=1)
Define(RENEWAL 108238)
    SpellInfo(RENEWAL cd=120)
Define(TYPHOON 132469)
    SpellInfo(TYPHOON cd=20)
Define(WILD_CHARGE 102401)
    SpellInfo(WILD_CHARGE cd=15)

#Glyphs
Define(GLYPH_OF_SHRED 114234)
Define(GLYPH_OF_SAVAGERY 127540)

# Shared spells
Define(FAERIE_FERAL 770)
    SpellInfo(FAERIE_FERAL duration=300 cd=6)
    SpellAddTargetDebuff(FAERIE_FERAL FAERIE_FERAL=1 WEAKENED_ARMOR=1)
Define(FAERIE_SWARM 102355)
    SpellInfo(FAERIE_SWARM duration=300 cd=6)
    SpellAddTargetDebuff(FAERIE_SWARM FAERIE_SWARM=1 WEAKENED_ARMOR=1)

# Cat spells
Define(BERSERK_CAT 106951) #cat cd buff
    SpellInfo(BERSERK_CAT duration=15 cd=180)
    SpellAddBuff(BERSERK_CAT BERSERK_CAT=1)
Define(CAT_FORM 768)
    SpellAddBuff(CAT_FORM CAT_FORM=1)
Define(FEROCIOUS_BITE 22568) #cat finish 25-50 energy
    SpellInfo(FEROCIOUS_BITE energy=25 combo=0)
   SpellInfo(FEROCIOUS_BITE base=500 bonuscp=762 bonusapcp=0.196)
Define(INCARNATION_CAT 102543)
    SpellInfo(INCARNATION_CAT duration=30 cd=180)
Define(MAIM 22570) #cat interrupt
    SpellInfo(MAIM cd=10 energy=35 combo=0)
Define(MANGLE_CAT 33876) #cat bleed+debuff
    SpellInfo(MANGLE_CAT inccounter=ripshreds energy=35 combo=1)
    SpellInfo(MANGLE_CAT critcombo=1 if_spell=PRIMAL_FURY)
Define(RAKE 1822) #cat bleed
    SpellInfo(RAKE combo=1 duration=15 energy=35 tick=3)
    SpellInfo(RAKE base=98.13 bonusap=0.31453)
    SpellInfo(RAKE critcombo=1 if_spell=PRIMAL_FURY)
    SpellAddTargetDebuff(RAKE RAKE=1)
    SpellDamageBuff(RAKE DREAM_OF_CENARIUS_DAMAGE=1.25)
Define(RAVAGE 6785)
    SpellInfo(RAVAGE inccounter=ripshreds energy=45 combo=1)
    SpellInfo(RAVAGE critcombo=1 if_spell=PRIMAL_FURY)
Define(RAVAGE_BANG 102545)
    SpellInfo(RAVAGE_BANG inccounter=ripshreds energy=0 combo=1)
    SpellInfo(RAVAGE_BANG critcombo=1 if_spell=PRIMAL_FURY)
Define(RIP 1079) #cat bleed
    SpellInfo(RIP resetcounter=ripshreds duration=16 energy=30 tick=2 combo=0)
    SpellInfo(RIP base=112.76 bonuscp=320 bonusapcp=0.0484) # damage coefficients
    SpellAddTargetDebuff(RIP RIP=1)
    SpellDamageBuff(RIP DREAM_OF_CENARIUS_DAMAGE=1.25)
Define(SAVAGE_ROAR_OLD 52610)
    SpellInfo(SAVAGE_ROAR_OLD duration=18 combo=0 energy=25)
    SpellAddBuff(SAVAGE_ROAR_OLD SAVAGE_ROAR_OLD=1)
Define(SAVAGE_ROAR_GLYPHED 127538)
    SpellInfo(SAVAGE_ROAR_GLYPHED duration=12 combo=-5 energy=25)
    SpellAddBuff(SAVAGE_ROAR_GLYPHED SAVAGE_ROAR_GLYPHED=1)
    SpellList(SAVAGE_ROAR 52610 127538)
Define(SHRED 5221) #cat behind
    SpellInfo(SHRED inccounter=ripshreds energy=40 combo=1)
    SpellInfo(SHRED critcombo=1 if_spell=PRIMAL_FURY)
Define(SHRED_BANG 114236)
    SpellInfo(SHRED_BANG inccounter=ripshreds energy=40 combo=1)
    SpellInfo(SHRED_BANG critcombo=1 if_spell=PRIMAL_FURY)
Define(STAMPEDE 81022)
    SpellAddBuff(STAMPEDE STAMPEDE=1)
Define(SKULL_BASH_CAT 80965) #cat interrupt
    SpellInfo(SKULL_BASH_CAT cd=15 energy=15)
Define(THRASH_CAT 106830)
    SpellInfo(THRASH_CAT duration=15 energy=50 tick=3)
    SpellAddTargetDebuff(THRASH_CAT THRASH_CAT=1 WEAKENED_BLOWS=1)
    SpellDamageBuff(THRASH_CAT DREAM_OF_CENARIUS_DAMAGE=1.25)
Define(TIGERS_FURY 5217) #cat buff
    SpellInfo(TIGERS_FURY duration=6 energy=-60 cd=30)
    SpellAddBuff(TIGERS_FURY TIGERS_FURY=1)

# Bear spells
Define(BEAR_FORM 5487)
    SpellAddBuff(BEAR_FORM BEAR_FORM=1)
Define(BERSERK_BEAR 106952) #cat+bear cd buff
    SpellInfo(BERSERK_BEAR duration=10 cd=180 )
    SpellAddBuff(BERSERK_BEAR BERSERK_BEAR=1)
Define(ENRAGE 5229)
Define(FRENZIED_REGEN 22842)
Define(INCARNATION_BEAR 102558)
    SpellInfo(INCARNATION_BEAR duration=30 cd=180 )
Define(LACERATE 33745)
Define(MANGLE_BEAR 33878)
    SpellInfo(MANGLE_BEAR cd=3 buffnocd=INCARNATION_BEAR buffnocd=BERSERK_BEAR)
Define(MAUL 6807)
Define(MIGHT_OF_URSOC 106922)
Define(SAVAGE_DEFENSE 62606)
Define(SURVIVAL_INSTINCTS 61336)
Define(SWIPE_BEAR 779)
Define(THRASH_BEAR 77758)
Define(TOOTH_AND_CLAW 135286)
    SpellAddBuff(TOOTH_AND_CLAW TOOTH_AND_CLAW=1)
Define(TOOTH_AND_CLAW_DEBUFF 135601)
    SpellAddTargetDebuff(TOOTH_AND_CLAW_DEBUFF TOOTH_AND_CLAW_DEBUFF=1)

###############
## Define Settings ##
###############

AddCheckBox(cooldownsL "Show Left Rotation Boxes" default)
AddCheckBox(cooldownsR "Show Right Cooldown Boxes" default)
AddCheckBox(altpredictive "Alternate predictive box")
AddCheckBox(bearaoe "Bear AOE Rotation")
AddCheckBox(cooldownsRatio "Show Rake and Rip Ratio Boxes" mastery=2)
AddCheckBox(lucioles SpellName(FAERIE_FERAL) default mastery=2)
AddCheckBox(berserk "Cat Berserk" default mastery=2)
AddCheckBox(infront "Frontal attack" mastery=2)
AddCheckBox(predictive "Hide predictive box" mastery=2)
AddCheckBox(nvbounce "Use healing CDs for damage" mastery=2)

################
## Helper Functions ##
################

# CritChance and Armor Functions
AddFunction MeleeCritChanceNoCap
{
    if BuffPresent(attack_power_multiplier any=1)
   {
       {CritChance() + {{{AttackPower()-240}/2770.9} + 5.44}}
   }
   unless BuffPresent(attack_power_multiplier any=1)
   {
       {CritChance() + {{{AttackPower()-218}/2519} + 5.44}}
   }
}
AddFunction MeleeCritChance
{
   if MeleeCritChanceNoCap() > 100
      100
   if MeleeCritChanceNoCap() < 0
      0
   unless MeleeCritChanceNoCap() > 100 and MeleeCritChanceNoCap() < 0
      MeleeCritChanceNoCap()
}
AddFunction LastRakeCritChanceNoCap
{
    if BuffPresent(attack_power_multiplier any=1)
   {
       {LastSpellCritChance(RAKE) + {{{LastSpellAttackPower(RAKE)-240}/2770.9} + 5.44}}
   }
   unless BuffPresent(attack_power_multiplier any=1)
   {
       {LastSpellCritChance(RAKE) + {{{LastSpellAttackPower(RAKE)-218}/2519} + 5.44}}
   }
}
AddFunction LastRakeCritChance
{
   if LastRakeCritChanceNoCap() > 100
      100
   if LastRakeCritChanceNoCap() < 0
      0
   unless LastRakeCritChanceNoCap() > 100 and LastRakeCritChanceNoCap() < 0
      LastRakeCritChanceNoCap()
}
AddFunction LastRipCritChanceNoCap
{
    if BuffPresent(attack_power_multiplier any=1)
   {
       {LastSpellCritChance(RIP) + {{{LastSpellAttackPower(RIP)-240}/2770.9} + 5.44}}
   }
   unless BuffPresent(attack_power_multiplier any=1)
   {
       {LastSpellCritChance(RIP) + {{{LastSpellAttackPower(RIP)-218}/2519} + 5.44}}
   }
}
AddFunction LastRipCritChance
{
   if LastRipCritChanceNoCap() > 100
      100
   if LastRipCritChanceNoCap() < 0
      0
   unless LastRipCritChanceNoCap() > 100 and LastRipCritChanceNoCap() < 0
      LastRipCritChanceNoCap()
}
AddFunction ArmorReduction # Calculates armor for level 88 and higher mobs;  Only verified accurate with bosses and 90 target dummies
{
   if target.Level(more 87) or target.Level(less 1)
   {
      if target.Level(less 1)
         {24835*{1-target.DebuffStacks(WEAKENED_ARMOR any=1)*0.04} / {24835*{1-target.DebuffStacks(WEAKENED_ARMOR any=1)*0.04} + 4037.5*90 - 317117.5}}
      unless target.Level(less 1)
         {{24835*{1-{93-target.Level()}*0.165}*{1-target.DebuffStacks(WEAKENED_ARMOR any=1)*0.04}} / {{24835*{1-{93-target.Level()}*0.165}*{1-target.DebuffStacks(WEAKENED_ARMOR any=1)*0.04}} + 4037.5*90 - 317117.5}}
         }
}

# Time till energy for abilities functions (must come before Rune functions)
AddFunction TimeTilEnergyForThrash
{
    if BuffExpires(BERSERK_CAT) {
        if Energy() <= 50 {
            { 50 - Energy() } / EnergyRegen()
        }
        unless Energy() <= 50 {
            0
        }
    }
    if BuffPresent(BERSERK_CAT) {
        if Energy() <= 25 {
            { 25 - Energy() } / EnergyRegen()
        }
        unless Energy() <= 25 {
            0
        }
    }
}
AddFunction TimeTilEnergyForRake
{
    if BuffExpires(BERSERK_CAT) {
        if Energy() <= 35 {
            { 35 - Energy() } / EnergyRegen()
        }
        unless Energy() <= 35 {
            0
        }
    }
    if BuffPresent(BERSERK_CAT) {
        if Energy() <= 18 {
            { 18 - Energy() } / EnergyRegen()
        }
        unless Energy() <= 18 {
            0
        }
    }
}
AddFunction TimeTilEnergyForRip
{
    if BuffExpires(BERSERK_CAT) {
        if Energy() <= 30 {
            { 30 - Energy() } / EnergyRegen()
        }
        unless Energy() <= 30 {
            0
        }
    }
    if BuffPresent(BERSERK_CAT) {
        if Energy() <= 15 {
            { 15 - Energy() } / EnergyRegen()
        }
        unless Energy() <= 15 {
            0
        }
    }
}
# Rake functions
AddFunction RakeTickDamage
{
    # rake_tick_damage = (tick_damage + coeff * AP) * damage_multiplier * bleed_multiplier
    {Damage(RAKE) * {1+{MeleeCritChance()/100}}} * {1 + Mastery()/100}
}
AddFunction LastRakeTickDamage
{
    # rake_tick_damage = (tick_damage + coeff * AP) * damage_multiplier * bleed_multiplier
    {LastSpellEstimatedDamage(RAKE) * {1+{LastRakeCritChance()/100}}} * {1 + LastSpellMastery(RAKE)/100}
}
AddFunction RakeRatio
{
    if TargetDebuffPresent(RAKE) {100 * RakeTickDamage()/LastRakeTickDamage()}
    unless TargetDebuffPresent(RAKE) 100.0
}
AddFunction RuneRakeUsableBeforeExpire # Checks to make sure you have the energy/cooldown to use a bleed before the Rune buff expires
{
   HasTrinket(ROR_ITEM) and BuffPresent(ROR_MASTERY) and BuffRemains(ROR_MASTERY) > {TimeTilEnergyForRake()+0.2} and BuffRemains(ROR_MASTERY) > {SpellCooldown(RAKE)+0.2}
}

# Rip functions
AddFunction RipTickDamage
{
    # Damage(rip) == { 113 + (320 * CP) + (0.3872 * AP * CP) } * DamageMultiplier(rip)
    {Damage(RIP) * {1+{MeleeCritChance()/100}}} * {1 + Mastery()/100}
}
AddFunction LastRipTickDamage
{
    # Damage(rip) == { 113 + (320 * CP) + (0.3872 * AP * CP) } * DamageMultiplier(rip)
    {LastSpellEstimatedDamage(RIP) * {1+{LastRipCritChance()/100}}} * {1 + LastSpellMastery(RIP)/100}
}
AddFunction RipRatio
{
    if TargetDebuffPresent(RIP) {100 * RipTickDamage()/LastRipTickDamage()}
    unless TargetDebuffPresent(RIP) 100.0
}
AddFunction RipDamageTillDead
{
    # The damage from Rip that is cast under the current conditions and lasting till target is dead.
    # Multiply the damage per tick with the number of ticks that can fit into the time to die.
    # XXX Should factor in crit somehow.
    {Damage(RIP) * {1+{MeleeCritChance()/100}}} * {1 + Mastery() / 100} * {target.TimeToDie() / 2}
}
AddFunction ExistingRipDamageTillDead
{
    # The damage from Rip that is already on the target and lasting till target is dead.
    if target.DebuffPresent(RIP)
    {
        # Multiply the damage per tick with the number of ticks that can fit into the time to die.
        # XXX Should factor in crit somehow.
        {LastSpellEstimatedDamage(RIP) * {1+{LastRipCritChance()/100}}} * {1 + LastSpellMastery(RIP) / 100} * {target.TimeToDie() / 2}
    }
    unless target.DebuffPresent(RIP)
    {
        0
    }
}
AddFunction LastRipDamageTillExpire # Damage from Rip already on target
{
    if target.DebuffPresent(RIP)
    {
        {LastSpellEstimatedDamage(RIP) * {1+{LastRipCritChance()/100}}} * {1 + LastSpellMastery(RIP) / 100}} * TicksRemain(RIP target=target)
    }
    unless target.DebuffPresent(RIP)
    {
        0
    }
}
AddFunction RipDamageTillExpire # New bleed over the duration of the current bleed
{
    {Damage(RIP) * {1+{MeleeCritChance()/100}}} * {1 + Mastery() / 100} * TicksRemain(RIP target=target)}
}
AddFunction RuneRipUsableBeforeExpire # Checks to make sure you have the energy/cooldown to use a bleed before the Rune buff expires
{
   HasTrinket(ROR_ITEM) and BuffPresent(ROR_MASTERY) and BuffRemains(ROR_MASTERY) > {TimeTilEnergyForRip()+0.2} and BuffRemains(ROR_MASTERY) > {SpellCooldown(RIP)+0.2}
}

# Ferocious Bite function
AddFunction FerociousBiteDamage
{
   if {1.25+{MeleeCritChance()/100}} > 100
      Damage(FEROCIOUS_BITE) * 2 * 2 * {1-ArmorReduction()}
   unless {1.25+{MeleeCritChance()/100}} > 100
      Damage(FEROCIOUS_BITE) * {1.25+{MeleeCritChance()/100}} * 2 * {1-ArmorReduction()}
}


# Misc functions
AddFunction FaerieFire
{
    if TalentPoints(FAERIE_SWARM_TALENT) Spell(FAERIE_SWARM)
    unless TalentPoints(FAERIE_SWARM_TALENT) Spell(FAERIE_FERAL)
}
AddFunction SavageRoar
{
    if Glyph(GLYPH_OF_SAVAGERY) Spell(SAVAGE_ROAR_GLYPHED)
    if Glyph(GLYPH_OF_SAVAGERY no) and ComboPoints(more 0) Spell(SAVAGE_ROAR_OLD)
}
AddFunction UsePotion
{
    #virmens_bite_potion
    if CheckBoxOn(potions) and target.Classification(worldboss) Item(virmens_bite_potion)
}



#############################
## Feral rotation functions (Mastery=2) ##
#############################

AddFunction NotInCombat
{
    unless InCombat() {
        if BuffExpires(str_agi_int 400 any=1) Spell(MARK_OF_THE_WILD)
        if BuffExpires(DREAM_OF_CENARIUS_DAMAGE) and TalentPoints(DREAM_OF_CENARIUS_TALENT) Spell(HEALING_TOUCH)
        unless Stance(3) Spell(CAT_FORM)
        if Glyph(GLYPH_OF_SAVAGERY) and ComboPoints() ==0 {
            if BuffRemains(SAVAGE_ROAR_GLYPHED) <15 and TimeToMaxEnergy() < BuffRemains(SAVAGE_ROAR_GLYPHED)-11.5
            or BuffRemains(SAVAGE_ROAR_GLYPHED) <9 and TimeToMaxEnergy() < BuffRemains(SAVAGE_ROAR_GLYPHED)-8.5
            or BuffRemains(SAVAGE_ROAR_GLYPHED) <6 and TimeToMaxEnergy() < BuffRemains(SAVAGE_ROAR_GLYPHED)-5.5
            or BuffRemains(SAVAGE_ROAR_GLYPHED) <3 and TimeToMaxEnergy() < BuffRemains(SAVAGE_ROAR_GLYPHED)-2.5
            or BuffExpires(SAVAGE_ROAR_GLYPHED) {
                SavageRoar()
            }
        }
        if TalentPoints(FORCE_OF_NATURE_TALENT) Spell(FORCE_OF_NATURE)
    }
}

AddFunction FillerActions {
    #thrash_cat,if=dot.thrash_cat.remains<3&target.time_to_die>=6&combo_points>=5
    if target.DebuffRemains(THRASH_CAT) <3 and target.TimeToDie() >=9 and ComboPoints() >=5 Spell(THRASH_CAT)
    if TalentPoints(INCARNATION_TALENT)
    {
        #ravage
        if BuffPresent(INCARNATION_CAT) Spell(RAVAGE)
    }
    if not TalentPoints(INCARNATION_TALENT) or BuffExpires(INCARNATION_CAT)
    {
        #actions.filler+=/shred,if=(buff.omen_of_clarity.react|buff.berserk.up|energy.regen>=15)&buff.king_of_the_jungle.down
        if BuffPresent(CLEARCASTING) or BuffPresent(BERSERK_CAT) or EnergyRegen() >=15
        and {not CheckBoxOn(infront)
            or {Glyph(GLYPH_OF_SHRED) and {BuffPresent(TIGERS_FURY) or BuffPresent(BERSERK_CAT)}}}
        Spell(SHRED)
        #actions.filler+=/mangle_cat,if=buff.king_of_the_jungle.down
        Spell(MANGLE_CAT)
    }
}

AddFunction SpareGcdCooldowns {
    if TalentPoints(FORCE_OF_NATURE_TALENT)
    {
        #treants
        Spell(FORCE_OF_NATURE)
    }
    # Spirit Wolves goes here when symbiosis is supported appropriately.
}

# Feral rotation for talent builds with "Dream of Cenarius".

AddFunction FillerConditionsDoC
{
    #run_action_list,name=filler,if=buff.omen_of_clarity.react
    if BuffPresent(CLEARCASTING) FillerActions()
    #run_action_list,name=filler,if=(combo_points<5&dot.rip.remains<3)|(combo_points=0&buff.savage_roar.remains<2)
    if {ComboPoints() <5 and target.DebuffRemains(RIP) <3} or {ComboPoints() ==0 and BuffRemains(SAVAGE_ROAR) <2} FillerActions()
    #run_action_list,name=filler,if=buff.predatory_swiftness.remains>1
    if BuffRemains(PREDATORY_SWIFTNESS) >1 FillerActions()
    #run_action_list,name=filler,if=target.time_to_die<=8.5
    if target.TimeToDie() <=8.5 FillerActions()
    #run_action_list,name=filler,if=buff.tigers_fury.up|buff.berserk.up
    if BuffPresent(TIGERS_FURY) or BuffPresent(BERSERK_CAT) FillerActions()
    #run_action_list,name=filler,if=cooldown.tigers_fury.remains<=3
    if SpellCooldown(TIGERS_FURY) <=3 FillerActions()
    #run_action_list,name=filler,if=energy.time_to_max<=1
    if TimeToMaxEnergy() <=1 FillerActions()
   if TalentPoints(SOUL_OF_THE_FOREST_TALENT) and not TalentPoints(DREAM_OF_CENARIUS_TALENT)
    {
        #run_action_list,name=filler,if=combo_points<5
        if ComboPoints() <5 FillerActions()
    }
}

AddFunction MainActionsDoC
{
    #auto_attack
   
    #healing_touch,if=buff.predatory_swiftness.up&buff.predatory_swiftness.remains<=1.5&buff.dream_of_cenarius_damage.down
    if TalentPoints(DREAM_OF_CENARIUS_TALENT) and BuffPresent(PREDATORY_SWIFTNESS) and BuffRemains(PREDATORY_SWIFTNESS) <=1.5 and BuffExpires(DREAM_OF_CENARIUS_DAMAGE) Spell(HEALING_TOUCH)
   
    #savage_roar,if=buff.savage_roar.down
    if BuffExpires(SAVAGE_ROAR) SavageRoar()
   
    #faerie_fire,if=debuff.weakened_armor.stack<3
    if target.DebuffStacks(WEAKENED_ARMOR any=1) <3 and CheckBoxOn(lucioles) FaerieFire()
   
    #healing_touch,if=buff.predatory_swiftness.up&combo_points>=4&buff.dream_of_cenarius_damage.down
    if TalentPoints(DREAM_OF_CENARIUS_TALENT) and BuffPresent(PREDATORY_SWIFTNESS) and BuffExpires(DREAM_OF_CENARIUS_DAMAGE) and ComboPoints() >=4 Spell(HEALING_TOUCH)
   
    #healing_touch,if=buff.natures_swiftness.up
    if TalentPoints(DREAM_OF_CENARIUS_TALENT) and BuffPresent(NATURES_SWIFTNESS) Spell(HEALING_TOUCH)
   
    unless target.InRange(MANGLE_CAT) Texture(ability_druid_catformattack)
   
    #incarnation,if=energy<=35&!buff.omen_of_clarity.react&cooldown.tigers_fury.remains=0&cooldown.berserk.remains=0
    #use_item,name=eternal_blossom_grips,sync=tigers_fury
    #tigers_fury,if=(energy<=35&!buff.omen_of_clarity.react)|buff.king_of_the_jungle.up
    #berserk,if=buff.tigers_fury.up|(target.time_to_die<15&cooldown.tigers_fury.remains>6)
    if {{Energy() <=35 and BuffExpires(CLEARCASTING)} or BuffPresent(INCARNATION_CAT)} and Spell(TIGERS_FURY)
    {
        if CheckBoxOn(berserk) and Spell(BERSERK_CAT)
        {
            if TalentPoints(INCARNATION_TALENT) Spell(INCARNATION)
            if not TalentPoints(INCARNATION_TALENT) or BuffPresent(INCARNATION_CAT) Spell(BERSERK_CAT)
        }
        unless BuffPresent(BERSERK_CAT) Spell(TIGERS_FURY)
    }
    if CheckBoxOn(berserk) and TalentPoints(INCARNATION_TALENT) and BuffPresent(BERSERK_CAT) Spell(INCARNATION_CAT)
   
    #ferocious_bite,if=combo_points>=1&dot.rip.ticking&dot.rip.remains<=3&target.health.pct<=25
    if target.HealthPercent() <=25 and ComboPoints() >=1 and target.DebuffPresent(RIP) and target.DebuffRemains(RIP) <=4 Spell(FEROCIOUS_BITE)
   
    #thrash_cat,if=target.time_to_die>=6&buff.omen_of_clarity.react&dot.thrash_cat.remains<3
    if target.TimeToDie() >=9 and BuffPresent(CLEARCASTING) and target.DebuffRemains(THRASH_CAT) <3 Spell(THRASH_CAT)
   
    #ferocious_bite,if=(target.time_to_die<=4&combo_points>=5)|(target.time_to_die<=1&combo_points>=3)
    if target.TimeToDie() <=4 and ComboPoints() >=5 Spell(FEROCIOUS_BITE)
    if target.TimeToDie() <=1 and ComboPoints() >=3 Spell(FEROCIOUS_BITE)
   
    if target.HealthPercent() <=25
    {
        #savage_roar,if=buff.savage_roar.remains<=3&combo_points>0&target.health.pct<25
        if BuffRemains(SAVAGE_ROAR) <=3 and ComboPoints() >0 SavageRoar()
       
        if ComboPoints() >=5
        {
            #natures_swiftness,if=buff.dream_of_cenarius_damage.down&buff.predatory_swiftness.down&combo_points>=5&target.health.pct<=25
            if TalentPoints(DREAM_OF_CENARIUS_TALENT) and TalentPoints(NATURES_SWIFTNESS_TALENT) and BuffExpires(DREAM_OF_CENARIUS_DAMAGE) and BuffExpires(PREDATORY_SWIFTNESS) and BuffRemains(SAVAGE_ROAR) >5 Spell(NATURES_SWIFTNESS)
           
            #virmens_bite_potion,if=combo_points>=5&$(time_til_bitw)<15&$(rip_ratio)>=1.15&buff.dream_of_cenarius_damage.up
            if not HasTrinket(ROR_ITEM) and ComboPoints() >=5 and BuffPresent(DREAM_OF_CENARIUS_DAMAGE) and RipRatio() >=115 UsePotion()
           
            #virmens_bite_potion,if=combo_points>=5&$(time_til_bitw)<15&buff.rune_of_reorigination.up&buff.dream_of_cenarius_damage.up
            if HasTrinket(ROR_ITEM) and ComboPoints() >=5 and BuffPresent(DREAM_OF_CENARIUS_DAMAGE) and BuffPresent(ROR_MASTERY) UsePotion()
           
            #virmens_bite_potion,if=target.time_to_die<=40
            if target.TimeToDie() <=40 UsePotion()
           
            #rip,line_cd=30,if=combo_points>=5&buff.virmens_bite_potion.up&buff.dream_of_cenarius_damage.up&target.health.pct<=25&target.time_to_die>30
            # Assume that FB will be 400% normal damage (100% increased damage + crit) to decide if we should overwrite Rip.
            if RipDamageTillDead() > {ExistingRipDamageTillDead() + FerociousBiteDamage()} Spell(RIP)
         
         
           
            #pool_resource,wait=0.25,if=combo_points>=5&dot.rip.ticking&target.health.pct<=25&((energy<50&buff.berserk.down)|(energy<25&buff.berserk.remains>1))
            #ferocious_bite,if=combo_points>=5&dot.rip.ticking&target.health.pct<=25
            if target.DebuffPresent(RIP)
            {
                unless {{BuffExpires(BERSERK_CAT) and Energy() >=50} or {BuffPresent(BERSERK_CAT) and Energy() >=25}} SpareGcdCooldowns()
                wait if {BuffExpires(BERSERK_CAT) and Energy() >=50} or {BuffPresent(BERSERK_CAT) and Energy() >=25} Spell(FEROCIOUS_BITE)
            }
        }
    }
   
    if RuneRipUsableBeforeExpire() and target.HealthPercent() >25 and ComboPoints() >=5 and target.TimeToDie() >30 and RipRatio() >=92
    {
        #natures_swiftness,if=enabled&buff.dream_of_cenarius_damage.down&buff.predatory_swiftness.down&combo_points>=5&$(rip_ratio)>=0.92&target.time_to_die>30
        if TalentPoints(DREAM_OF_CENARIUS_TALENT) and BuffRemains(ROR_MASTERY) >1.5 and TalentPoints(NATURES_SWIFTNESS_TALENT) and BuffExpires(DREAM_OF_CENARIUS_DAMAGE) and BuffExpires(PREDATORY_SWIFTNESS) Spell(NATURES_SWIFTNESS)
       
        #rip,if=combo_points>=5&$(rip_ratio)>=1.15&target.time_to_die>30
        if RipRatio() >=115 Spell(RIP)
    }
   
    #rip,if=combo_points>=5&target.time_to_die>=6&dot.rip.remains<2&buff.dream_of_cenarius_damage.up
    if TalentPoints(DREAM_OF_CENARIUS_TALENT) and target.TimeToDie() >=6 and ComboPoints() >=5 and target.DebuffRemains(RIP) <2 and BuffPresent(DREAM_OF_CENARIUS_DAMAGE) Spell(RIP)
   
    #rip,if=combo_points>=5&target.time_to_die>=6&dot.rip.remains<6.0&buff.dream_of_cenarius_damage.up&dot.rip.multiplier<=tick_multiplier
    if TalentPoints(DREAM_OF_CENARIUS_TALENT) and target.TimeToDie() >=6 and ComboPoints() >=5 and target.DebuffRemains(RIP) <6 and BuffPresent(DREAM_OF_CENARIUS_DAMAGE) and RipRatio() >=100 Spell(RIP)
   
    #natures_swiftness,if=buff.dream_of_cenarius_damage.down&buff.predatory_swiftness.down&combo_points>=5&dot.rip.remains<3&(buff.berserk.up|dot.rip.remains+1.9<=cooldown.tigers_fury.remains)
    if TalentPoints(DREAM_OF_CENARIUS_TALENT) and TalentPoints(NATURES_SWIFTNESS_TALENT) and BuffExpires(DREAM_OF_CENARIUS_DAMAGE)
    and BuffExpires(PREDATORY_SWIFTNESS) and ComboPoints() >=5 and target.DebuffRemains(RIP) <3
    and {BuffPresent(BERSERK_CAT) or target.DebuffRemains(RIP) +1.9 <= SpellCooldown(TIGERS_FURY)}
    {
        Spell(NATURES_SWIFTNESS)
    }
   
    #rip,if=combo_points>=5&target.time_to_die>=6&dot.rip.remains<2&(buff.berserk.up|dot.rip.remains+1.9<=cooldown.tigers_fury.remains)
    if target.TimeToDie() >=6 and ComboPoints() >=5 and target.DebuffRemains(RIP) <2
    and {BuffPresent(BERSERK_CAT) or {target.DebuffRemains(RIP)+1.9} <=SpellCooldown(TIGERS_FURY)}
    {
        Spell(RIP)
    }
   
    #savage_roar,if=buff.savage_roar.remains<=3&combo_points>0&buff.savage_roar.remains+2>dot.rip.remains
    if BuffRemains(SAVAGE_ROAR) <=3 and ComboPoints() >0 and {BuffRemains(SAVAGE_ROAR) +2 > target.DebuffRemains(RIP)} SavageRoar()
   
    #savage_roar,if=buff.savage_roar.remains<=6&combo_points>=5&buff.savage_roar.remains+2<=dot.rip.remains
    if BuffRemains(SAVAGE_ROAR) <=6 and ComboPoints() >=5 and {BuffRemains(SAVAGE_ROAR) +2 <= target.DebuffRemains(RIP)} SavageRoar()
   
   # If the old rip + 50 energy FB is worse than the bleed of the new rip over the old rip's duration, do a Rip instead of a FB;  Should rarely if ever be true unless you screw up
   if {RipDamageTillExpire() > {LastRipDamageTillExpire() + FerociousBiteDamage()}} and {ComboPoints() >= 5} Spell(RIP)
   
    #actions.doc+=/pool_resource,wait=0.1,if=combo_points>=5&((energy<50&buff.berserk.down)|(energy<25&buff.berserk.remains>1))&dot.rip.ticking&!(dot.rip.remains-2<=energy.time_to_max-1)&!(buff.savage_roar.remains-3<=energy.time_to_max-1)
    #actions.doc+=/ferocious_bite,if=combo_points>=5&dot.rip.ticking&!(dot.rip.remains-2<=energy.time_to_max-1)&!(buff.savage_roar.remains-3<=energy.time_to_max-1)&!((buff.savage_roar.remains-6<=energy.time_to_max-1)&buff.savage_roar.remains+2<=$(rip_remains))
    if ComboPoints() >=5 and target.DebuffPresent(RIP) and BuffPresent(SAVAGE_ROAR)
    and not target.DebuffRemains(RIP)-2 <= TimeToMaxEnergy()-1
    and not BuffRemains(SAVAGE_ROAR)-3 <= TimeToMaxEnergy()-1
    and not {BuffRemains(SAVAGE_ROAR)-6 <= TimeToMaxEnergy()-1
        and BuffRemains(SAVAGE_ROAR)+2 <= target.DebuffRemains(RIP)}
    and target.DebuffRemains(RIP) >=5
    {
        unless {{BuffExpires(BERSERK_CAT) and Energy() >=50} or {BuffPresent(BERSERK_CAT) and Energy() >=25}} SpareGcdCooldowns()
        wait if {BuffExpires(BERSERK_CAT) and Energy() >=50} or {BuffPresent(BERSERK_CAT) and Energy() >=25} Spell(FEROCIOUS_BITE)
    }
   
    if target.TimeToDie() - target.DebuffRemains(RAKE) >3
    {
    if RuneRakeUsableBeforeExpire()
    {
        #rake,if=buff.rune_of_reorigination.up&$(rake_ratio)>=1
            if RakeRatio() >=100 Spell(RAKE)
   
            #rake,if=buff.rune_of_reorigination.up&dot.rake.remains<9&(buff.rune_of_reorigination.remains<=1.5)
            if target.DebuffRemains(RAKE) <9 and BuffRemains(ROR_MASTERY) <=1.5 Spell(RAKE)
    }
    if RuneRakeUsableBeforeExpire() or not BuffPresent(ROR_MASTERY)
   {
        #rake,if=target.time_to_die-dot.rake.remains>3&dot.rake.remains<6.0&buff.dream_of_cenarius_damage.up&dot.rake.multiplier<=tick_multiplier
        if TalentPoints(DREAM_OF_CENARIUS_TALENT) and target.DebuffRemains(RAKE) <6 and BuffPresent(DREAM_OF_CENARIUS_DAMAGE) and RakeRatio() >=100 Spell(RAKE)
       
        #rake,if=target.time_to_die-dot.rake.remains>3&tick_multiplier%dot.rake.multiplier>1.12
        if RakeRatio() >=112 Spell(RAKE)
    }
        #rake,if=target.time_to_die-dot.rake.remains>3&dot.rake.remains<3.0&(buff.berserk.up|(cooldown.tigers_fury.remains+0.8)>=dot.rake.remains|energy>60)
        if target.DebuffRemains(RAKE) <3
        and {BuffPresent(BERSERK_CAT)
            or Energy(more 60)
            or {SpellCooldown(TIGERS_FURY) +0.8 } >=target.DebuffRemains(RAKE)}
        Spell(RAKE)
      
   }
   
    #pool_resource,wait=0.25,for_next=1
    #thrash_cat,if=dot.thrash_cat.remains<3&target.time_to_die>=6&(dot.rip.remains>=4|buff.berserk.up)
    if target.DebuffRemains(THRASH_CAT) < {3 + TimeTilEnergyForThrash()}
    and target.TimeToDie() - TimeTilEnergyForThrash() >=9
    and {target.DebuffRemains(RIP) - TimeTilEnergyForThrash() >=4
        or BuffPresent(BERSERK_CAT)}
    {
        if TimeTilEnergyForThrash() >=1.5 SpareGcdCooldowns()
        wait if {BuffExpires(BERSERK_CAT) and Energy() >=50} or {BuffPresent(BERSERK_CAT) and Energy() >=25} Spell(THRASH_CAT)
    }
}

AddFunction Prediction
{
    if Stance(3) {
        if TalentPoints(DREAM_OF_CENARIUS_TALENT)
        {
            MainActionsDoC()
        }
        unless TalentPoints(DREAM_OF_CENARIUS_TALENT)
        {
            MainActionsDoC()
        }
    }
    if Stance(1) {
        if BuffPresent(HEART_OF_THE_WILD) Spell(FRENZIED_REGEN)
        unless BuffPresent(HEART_OF_THE_WILD) Spell(CAT_FORM)
    }
    if Stance(0) {
        if BuffPresent(HEART_OF_THE_WILD) {
            if BuffExpires(HEART_OF_THE_WILD) Texture(spell_holy_blessingofagility)
        }
        unless BuffPresent(HEART_OF_THE_WILD) Spell(CAT_FORM)
    }
    if Stance(4) or Stance(2) Spell(CAT_FORM)
}

#####################
## Feral icons (Mastery=2) ##
#####################
AddIcon help=Rake size=small mastery=2 checkboxon=cooldownsRatio
{
    RakeRatio()
}

AddIcon help=Rip size=small mastery=2 checkboxon=cooldownsRatio
{
    RipRatio()
}

AddIcon help=cd size=small mastery=2 checkboxon=cooldownsL {
    if target.InRange(SKULL_BASH_CAT) Spell(SKULL_BASH_CAT)
    unless target.Classification(worldboss)
    {
        if TalentPoints(MIGHTY_BASH_TALENT) and target.InRange(MIGHTY_BASH) Spell(MIGHTY_BASH)
        if TalentPoints(TYPHOON_TALENT) and target.InRange(SKULL_BASH_CAT) Spell(TYPHOON)
        if ComboPoints() >0 and target.InRange(MAIM) Spell(MAIM)
    }
    Spell(WILD_CHARGE)
}

AddIcon help=cd size=small mastery=2 checkboxon=cooldownsL { # Berserk Icon
    if BuffPresent(TIGERS_FURY) Spell(BERSERK_CAT)
    if 0s before Spell(BERSERK_CAT) Texture(Ability_mount_polarbear_white)
}


# Predictive rotation
AddIcon help=predictive size=small mastery=2 checkboxon=altpredictive {
    Prediction()
}

AddIcon help=extraCD size=small mastery=2 checkboxon=altpredictive {   
    if TalentPoints(HEART_OF_THE_WILD_TALENT) Spell(HEART_OF_THE_WILD)
    if TalentPoints(DREAM_OF_CENARIUS_TALENT) {
        if TalentPoints(NATURES_SWIFTNESS_TALENT) Spell(NATURES_SWIFTNESS)
        if TalentPoints(CENARION_WARD_TALENT) Spell(CENARION_WARD)
        if TalentPoints(RENEWAL_TALENT) Spell(RENEWAL)
    }
    if TalentPoints(NATURES_VIGIL_TALENT) Spell(NATURES_VIGIL)
}

# Main rotation
AddIcon help=main mastery=2 {
    NotInCombat()
    if Stance(3) {
        if TalentPoints(DREAM_OF_CENARIUS_TALENT)
        {
            MainActionsDoC()
            FillerConditionsDoC()
            SpareGcdCooldowns()
        }
        unless TalentPoints(DREAM_OF_CENARIUS_TALENT)
        {
            MainActionsDoC()
            FillerConditionsDoC()
            SpareGcdCooldowns()
        }
    }
    if Stance(1) {
        if BuffPresent(HEART_OF_THE_WILD) {
            if CheckBoxOff(bearaoe) BearMain()
            if CheckBoxOn(bearaoe) BearMainAOE()
        }
        unless BuffPresent(HEART_OF_THE_WILD) Spell(CAT_FORM)
    }
    if Stance(0) {
        if BuffPresent(HEART_OF_THE_WILD) {
            if CastTime(WRATH) <BuffRemains(HEART_OF_THE_WILD) Spell(WRATH)
            Spell(CAT_FORM)
        }
        unless BuffPresent(HEART_OF_THE_WILD) Spell(CAT_FORM)
    }
    if Stance(4) or Stance(2) Spell(CAT_FORM)
}

# Predictive rotation
AddIcon help=predictive mastery=2 checkboxoff=predictive {
    Prediction()
}

AddIcon help=cd size=small mastery=2 checkboxon=cooldownsR { # Rake
    if BuffExpires(SAVAGE_ROAR) Texture(ability_druid_skinteeth)
}
AddIcon help=cd size=small mastery=2 checkboxon=cooldownsR { # Rip
    if TargetDebuffExpires(RIP) Texture(ability_ghoulfrenzy)
}
AddIcon help=cd size=small mastery=2 checkboxon=cooldownsR { # Rake
    if TargetDebuffExpires(RAKE) Texture(ability_druid_disembowel)
}
AddIcon help=cd size=small mastery=2 checkboxon=cooldownsR {
    Spell(TIGERS_FURY)
}

################################
## Guardian rotation functions (Mastery=3) ##
################################

AddFunction BearMain {
    if Threat() <100 and target.DebuffPresent(LACERATE) and target.DebuffRemains(LACERATE) <2 Spell(LACERATE)
    if Threat() <100 and BuffPresent(INCARNATION_BEAR) and target.DebuffRemains(THRASH_BEAR) <2 Spell(THRASH_BEAR)
   
    Spell(MANGLE_BEAR)
   
   
    # Debuff maintenance.
    if target.DebuffRemains(WEAKENED_BLOWS 3 any=1) <=3 Spell(THRASH_BEAR)
    if target.DebuffRemains(WEAKENED_ARMOR 3 any=1) <=3 or target.DebuffStacks(WEAKENED_ARMOR any=1) <3
    {
        FaerieFire()
    }
   
    Spell(LACERATE)
    if target.DebuffPresent(THRASH_BEAR 6) FaerieFire()
    Spell(THRASH_BEAR)
}

AddFunction BearMainAOE {
    Spell(MANGLE_BEAR)
    Spell(THRASH_BEAR)
    Spell(SWIPE_BEAR)
}

#######################
## Guardian icons (Mastery=3) ##
#######################

AddIcon help=Rake size=small mastery=3 checkboxon=cooldownsRatio
{
    # Offset the guardian icons if the user has bleed ratios enabled
}

AddIcon help=Rip size=small mastery=3 checkboxon=cooldownsRatio
{
    # Offset the guardian icons if the user has bleed ratios enabled
}

AddIcon help=cd size=small mastery=3 checkboxon=cooldownsL {
    Spell(BARKSKIN)
}

AddIcon help=cd size=small mastery=3 checkboxon=cooldownsL {
    if TalentPoints(NATURES_SWIFTNESS_TALENT) Spell(NATURES_SWIFTNESS)
    if TalentPoints(RENEWAL_TALENT) Spell(RENEWAL)
    if TalentPoints(CENARION_WARD_TALENT) Spell(CENARION_WARD)
}

AddIcon mastery=3 size=small checkboxon=altpredictive checkboxoff=cooldownsL {
    if Rage(less 11) Spell(ENRAGE useable=1)
    Spell(SAVAGE_DEFENSE usable=1)
    Spell(FRENZIED_REGEN)
}

AddIcon mastery=3 size=small checkboxon=altpredictive checkboxoff=cooldownsL {
    if BuffPresent(TOOTH_AND_CLAW) and target.DebuffExpires(TOOTH_AND_CLAW_DEBUFF) Spell(TOOTH_AND_CLAW)
    unless BuffPresent(TOOTH_AND_CLAW) and target.DebuffExpires(TOOTH_AND_CLAW_DEBUFF) Spell(MAUL)
}

# Main rotation
AddIcon help=main mastery=3 {
    if InCombat(no) and BuffRemains(str_agi_int any=1) <400 Spell(MARK_OF_THE_WILD)
    unless Stance(1) Spell(BEAR_FORM)
   
    if CheckBoxOff(bearaoe) BearMain()
    if CheckBoxOn(bearaoe) BearMainAOE()
}

AddIcon help=defense mastery=3 checkboxoff=altpredictive {
    if Rage(less 11) Spell(ENRAGE useable=1)
    Spell(SAVAGE_DEFENSE usable=1)
    Spell(FRENZIED_REGEN)
}

AddIcon help=cd size=small mastery=3 checkboxon=cooldownsR {
    Spell(SURVIVAL_INSTINCTS)
}

AddIcon help=cd size=small mastery=3 checkboxon=cooldownsR {
    Spell(MIGHT_OF_URSOC)
}

AddIcon help=cd size=small mastery=3 checkboxon=cooldownsR {
    if TalentPoints(INCARNATION_TALENT) Spell(INCARNATION_BEAR)
    if TalentPoints(FORCE_OF_NATURE_TALENT) Spell(FORCE_OF_NATURE)
}

AddIcon help=cd size=small mastery=3 checkboxon=cooldownsR {
    Spell(BERSERK_BEAR)
}


Is my current script.

** Edited by Leafkiller to put spoiler tags around the code section so it is not painful for people scrolling through the thread

Exalted
User avatar
Posts: 1201
Joined: Fri Nov 25, 2011 7:49 pm

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby aggixx » Thu Jul 04, 2013 2:27 am

ShmooDude wrote:Honestly, I bet it wouldn't even trigger in SimCraft. I have yet to see it trigger in 3 hours of raiding tonight. Mostly I put it there as a fail safe to catch when I lag and put up 1 CP rips because of the tier bonus. Its only one line so it'd be easy for you to take out anyhow. On the plus side, the execute range rip logic does seem to work. I'll clean it up a little then either post it here or pm it later tonight.

I misread what you posted originally, didn't realize you meant the remaining duration of the current rip in both cases. I'll take a look.
Image

Revered
Posts: 319
Joined: Tue Feb 08, 2011 5:51 pm

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby ShmooDude » Thu Jul 04, 2013 6:30 am

Hey, question. How come in the Simcraft rotation it will reapply any rip of a ratio of >1.15 but in ovale we only do this under Rune proc?

Doing it the ovale way seems to be a DPS downgrade in Simcraft (not by much, like 500 dps), thought there may be some nuance of simcraft that I'm missing?

Exalted
User avatar
Posts: 1201
Joined: Fri Nov 25, 2011 7:49 pm

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby aggixx » Fri Jul 05, 2013 8:36 pm

aggixx wrote:Yeah, you're completely right. There's actually a line for that in the SimC script (or at least there was) that was basically "if=$(rip_ratio)>=1.15". That was originally added to handle Rune procs but I could never get a higher DPS figure out of specifying rune buff or changing the value up or down. I added it to a local version of the Ovale script at one point but I ended up taking it out because it was just really weird, would have so many posts going "WTFs why is telling me to rip all the god damn time". There's also probably a better measure of comparison than just saying "if the new rip is at least 15% stronger", perhaps one that would seem to make more sense.
Image

Revered
Posts: 319
Joined: Tue Feb 08, 2011 5:51 pm

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby ShmooDude » Fri Jul 05, 2013 9:10 pm

Yah I did a lot of screwing around with simcraft for the rip stuff and couldn't find a better method than simply rip ratio > 1.15
EDIT: Just ran another sim comparing just these two. Doing it only during Rune results in more FB but lower Rip uptime and average damage and lower overall DPS.

Question. I'm trying to eliminate the case where the script suggests Rip because its expiring and its just slightly too long for TF, but because we won't have the energy to do Rip before its time to wait on TF, it'll suggest rip for a second or two then swap away from it

I *think* its as simple as changing:

Code: Select all
{target.DebuffRemains(RIP)+1.9)} <=SpellCooldown(TIGERS_FURY)}


To:

Code: Select all
{target.DebuffRemains(RIP)+1.9+TimeTilEnergyForRip()} <=SpellCooldown(TIGERS_FURY)}


It does eliminate the suggestion and it seems to work at least as best as I can test it (trying to finagle around with energy and exact right timings of TF is rough, lol), I just wanna make sure I'm not overshooting it.


EDIT2: I've manged to add agility procs (dancing steel) and Tiger's Fury code similar to the Rune expiration code (ie if you won't have enough energy to put up a bleed before they expire). What I did is I calculated the ratio increase that the proc will give and remove it directly from the bleed ratio such that all calculations will work assuming that buff is no longer there (since it won't be barring getting a new proc). The question is, is this the ratio I should display (my inclination is yes) or should I display the actual ratio to the player (which would be very easy to do, just make a new function for it)?

Trinket procs will be a little more complicated due to them varying so much (5 diff ilvls with 3 potential upgrade states each) and I will have to make some assumptions as to whether they're upgraded or not.

Honored
Posts: 188
Joined: Tue Dec 14, 2010 5:34 pm

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby Jeshu » Sat Jul 06, 2013 5:02 pm

I will be releasing a beta of Ovale soon that has a few changes that should prove useful for @aggixx. DoTs should snapshot stats properly (needs more testing with FB refreshing Rip in execute range). You can query the snapshotted stats of a DoT by doing, e.g.,:
Code: Select all
# Re-apply Rip unless the existing Rip is way better.
unless target.DebuffMastery(rip) /Mastery() >1.5
    or target.DebuffAttackPower(rip) /AttackPower() >1.5
{
    Spell(rip)
}

This would allow for tracking something like the RoR proc without explicitly checking for the RoR trinket or the buff by making the decision based on mastery ratios.

There is support for doing something like the following, which uses a script-defined function to compute Damage():
Code: Select all
Define(rip 1079)
    SpellInfo(rip combo=0 duration=16 energy=30 tick=2)
    SpellInfo(rip damage=RipTickDamage)
    SpellDamageBuff(rip dream_of_cenarius_damage=1.25)
    SpellAddTargetDebuff(rip rip=1)

# Damage(rip) = RipTickDamage()
AddFunction RipTickDamage
{
    {14.125 + 40 * ComboPoints() + 0.0484 * AttackPower() * ComboPoints()}
        * DamageMultiplier(rip) * {1 + Mastery()}
}

In theory, you could write a FerociousBiteDamage() function that takes into account armor damage reduction by testing for the presence of various debuffs on the target.

I'm working on adding MH/OH weapon damage into the normal Damage() calculations as well as exposing these values via script conditions.

Please let me know if Ovale needs more features to implement the current feral DPS rotation.

Revered
Posts: 319
Joined: Tue Feb 08, 2011 5:51 pm

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby ShmooDude » Sat Jul 06, 2013 6:04 pm

Jeshu wrote:I will be releasing a beta of Ovale soon that has a few changes that should prove useful for @aggixx. DoTs should snapshot stats properly (needs more testing with FB refreshing Rip in execute range). You can query the snapshotted stats of a DoT by doing, e.g.,:
Code: Select all
# Re-apply Rip unless the existing Rip is way better.
unless target.DebuffMastery(rip) /Mastery() >1.5
    or target.DebuffAttackPower(rip) /AttackPower() >1.5
{
    Spell(rip)
}

This would allow for tracking something like the RoR proc without explicitly checking for the RoR trinket or the buff by making the decision based on mastery ratios.


Not sure how this is a change over the current? How does it differ from how I'd code it now (to get that exact functionality) which would be:

Code: Select all
# Re-apply Rip unless the existing Rip is way better.
unless LastSpellMastery(rip) /Mastery() >1.5
    or LastSpellMastery(rip) /AttackPower() >1.5
{
    Spell(rip)
}


Does it track it across multiple targets? That's the only thing I can figure that's different (and would defiantly be useful =D )

Jeshu wrote:There is support for doing something like the following, which uses a script-defined function to compute Damage():
Code: Select all
Define(rip 1079)
    SpellInfo(rip combo=0 duration=16 energy=30 tick=2)
    SpellInfo(rip damage=RipTickDamage)
    SpellDamageBuff(rip dream_of_cenarius_damage=1.25)
    SpellAddTargetDebuff(rip rip=1)

# Damage(rip) = RipTickDamage()
AddFunction RipTickDamage
{
    {14.125 + 40 * ComboPoints() + 0.0484 * AttackPower() * ComboPoints()}
        * DamageMultiplier(rip) * {1 + Mastery()}
}

In theory, you could write a FerociousBiteDamage() function that takes into account armor damage reduction by testing for the presence of various debuffs on the target.


I actually did exactly that ;) Though being able to call it the same as Ovale's innate function will be nice, good feature. Question, on that code block I notice that you have the SpellInfo line before the actual function itself. Will this work? I know that anytime I've tried to call a function before its been defined has not worked in the past (so many of my functions are intertwined at the moment I have to be careful of the order I put em in).

Jeshu wrote:I'm working on adding MH/OH weapon damage into the normal Damage() calculations as well as exposing these values via script conditions.


Yay! Lookin' forward to it.

Jeshu wrote:Please let me know if Ovale needs more features to implement the current feral DPS rotation.


Melee Critical Strike chance. I've managed to extract it from the data I have (using the CritChance() and AttackPower() functions, extracting the agi from attack power and getting the needed number from that), but its a complicated function and makes certain assumptions (assumes a certain amount of str and int). An innate function that pulls the actual number would be appreciated.

Armor support is a would be nice, but not 100% necessary and I'd say it should be low priority over other things. :)



Also, Nature's Vigil is still 1.2 in the code (v 5.3.2) and should be 1.1 (bliz halved the damage bonus and halved the cooldown so it wasn't so bursty).

EDIT: I've also found a rather odd bug.
Spoiler: show
Currently if we use the function:
Code: Select all
AddFunction TimeTilEnergyForThrash
{
     if Energy() <= 50 {
         { 50 - Energy() } / EnergyRegen()
     }
     unless Energy() <= 50 {
         { 50 - Energy() } / EnergyRegen() #actually should be zero, but just using to demonstrate the bug
     }
}

... and call it in something like this...
Code: Select all
AddIcon
{
    if target.DebuffRemains(THRASH_CAT) < {3 + OldTimeTillEnergyForThrash()}
    {
        Spell(THRASH_CAT)   
    }
}


It will only show the icon when the if statement becomes true. Where as if you do this instead:
Code: Select all
AddIcon
{
    if target.DebuffRemains(THRASH_CAT) < {3}
    {
        Spell(THRASH_CAT)   
    }
}

It will show the icon full time (with the appropriate countdown till its time to do Thrash).

It appears to be the if test interacting with the dynamicness of "{ 50 - Energy() } / EnergyRegen()" is the problem as I can tell. Either removing the if and unless and simply returning "{ 50 - Energy() } / EnergyRegen()" works as well as changing "{ 50 - Energy() } / EnergyRegen()" to a static number. Not sure why this is, its defiantly weird.

Honored
Posts: 188
Joined: Tue Dec 14, 2010 5:34 pm

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby Jeshu » Sat Jul 06, 2013 7:11 pm

ShmooDude wrote:Does it track it across multiple targets? That's the only thing I can figure that's different (and would defiantly be useful =D )

Yes, it checks the DoT on the named target, so it's more accurate and allows for multiple targets.

ShmooDude wrote:Question, on that code block I notice that you have the SpellInfo line before the actual function itself. Will this work? I know that anytime I've tried to call a function before its been defined has not worked in the past (so many of my functions are intertwined at the moment I have to be careful of the order I put em in).

Yes, it works because in this instance, the damage=FUNCNAME param just specifies the name of the function to call -- it does not evaluate the function at that time.

ShmooDude wrote:Melee Critical Strike chance. I've managed to extract it from the data I have (using the CritChance() and AttackPower() functions, extracting the agi from attack power and getting the needed number from that), but its a complicated function and makes certain assumptions (assumes a certain amount of str and int). An innate function that pulls the actual number would be appreciated.

There will be full suite of script conditions to pull the various bits of player stats. These were already suggested by @aggixx in a ticket on GitHub.
ShmooDude wrote:EDIT: I've also found a rather odd bug.
Spoiler: show
Currently if we use the function:
Code: Select all
AddFunction TimeTilEnergyForThrash
{
     if Energy() <= 50 {
         { 50 - Energy() } / EnergyRegen()
     }
     unless Energy() <= 50 {
         { 50 - Energy() } / EnergyRegen() #actually should be zero, but just using to demonstrate the bug
     }
}

... and call it in something like this...
Code: Select all
AddIcon
{
    if target.DebuffRemains(THRASH_CAT) < {3 + OldTimeTillEnergyForThrash()}
    {
        Spell(THRASH_CAT)   
    }
}


It will only show the icon when the if statement becomes true. Where as if you do this instead:
Code: Select all
AddIcon
{
    if target.DebuffRemains(THRASH_CAT) < {3}
    {
        Spell(THRASH_CAT)   
    }
}

It will show the icon full time (with the appropriate countdown till its time to do Thrash).

It appears to be the if test interacting with the dynamicness of "{ 50 - Energy() } / EnergyRegen()" is the problem as I can tell. Either removing the if and unless and simply returning "{ 50 - Energy() } / EnergyRegen()" works as well as changing "{ 50 - Energy() } / EnergyRegen()" to a static number. Not sure why this is, its defiantly weird.

The parser barfs on {3} because the braces are unnecessary. This is a side-effect of how the parser is coded and difficult to fix in the current implementation.

Revered
Posts: 319
Joined: Tue Feb 08, 2011 5:51 pm

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby ShmooDude » Sat Jul 06, 2013 7:43 pm

Jeshu wrote:The parser barfs on {3} because the braces are unnecessary. This is a side-effect of how the parser is coded and difficult to fix in the current implementation.


I think you misunderstood (the {3} works fine, the {3+xxx} doesn't). Looks like I was wrong about the cause in my previous post. The following also does not work:

Code: Select all
AddIcon
{
    if target.DebuffRemains(THRASH_CAT) < 3 + 50 / EnergyRegen() - Energy() / EnergyRegen()
        Spell(THRASH_CAT)
}

and functions identically (also broken) to
Code: Select all
AddIcon
{
    if target.DebuffRemains(THRASH_CAT) < {3 + 50 / EnergyRegen() - Energy() / EnergyRegen()}
        Spell(THRASH_CAT)
}


EDIT: However if you change the - to a +, it suddenly does work (though no longer calculates what its supposed to). The simplest equation I've found that reproduces the bug is:
Code: Select all
AddIcon
{
    if target.DebuffRemains(THRASH_CAT) > Energy() / EnergyRegen()
        Spell(THRASH_CAT)
}

or
Code: Select all
AddIcon
{
    if target.DebuffRemains(THRASH_CAT) < 0 - Energy() / EnergyRegen()
        Spell(THRASH_CAT)
}

Since those are both functionally identical. As far as what could be causing it I honestly have no clue, its very weird. Unfortunately it also affects the built in TimeToMaxEnergy() function.



EDIT3: Oh, one more thing, would it be possible to add functions that I can send variables too? Right now I have a LOT of near identical functions, but since I can't actually send them variables I have to actually have them all. Example:
Code: Select all
AddFunction RuneRakeUsableBeforeExpire # Checks to make sure you have the energy/cooldown to use a bleed before the Rune buff expires
{
   if Energy() >= EnergyForRake()
      BuffPresent(ROR_MASTERY) and {BuffRemains(ROR_MASTERY) > {0.2} or BuffPresent(CLEARCASTING)} and BuffRemains(ROR_MASTERY) > {SpellCooldown(RAKE)+0.2}
   unless Energy() >= EnergyForRake()
      BuffPresent(ROR_MASTERY) and {BuffRemains(ROR_MASTERY) > {TimeTilEnergyForRake()+0.2} or BuffPresent(CLEARCASTING)} and BuffRemains(ROR_MASTERY) > {SpellCooldown(RAKE)+0.2}
}
AddFunction TigersFuryRakeUsableBeforeExpire # Checks to make sure you have the energy/cooldown to use a bleed before the Rune buff expires
{
   if Energy() >= EnergyForRake()
      BuffPresent(TIGERS_FURY) and {BuffRemains(TIGERS_FURY) > {0.2} or BuffPresent(CLEARCASTING)} and BuffRemains(TIGERS_FURY) > {SpellCooldown(RAKE)+0.2}
   unless Energy() >= EnergyForRake()
      BuffPresent(TIGERS_FURY) and {BuffRemains(TIGERS_FURY) > {TimeTilEnergyForRake()+0.2} or BuffPresent(CLEARCASTING)} and BuffRemains(TIGERS_FURY) > {SpellCooldown(RAKE)+0.2}
}


This could easily be condensed down to one function if I could send variables (I have like 4 of these just for rake and then 4 more for rip, they could potentially be one function if I could send variables to em). If its already doable I apologize, but I haven't seen anything in the documentation.

EDIT4: There is a way around the bug. I did the following:

Code: Select all
AddFunction TimeTilEnergyForThrash
{
   { 50 - Energy() } / EnergyRegen()
}
AddIcon
{
   if {Energy() < 50 and target.DebuffRemains(THRASH_CAT) < {3 + TimeTilEnergyForThrash()} #This part works fine
   or {Energy() >= 50 and target.DebuffRemains(THRASH_CAT) < {3} #Had to remove the energy function for this to work
        Spell(THRASH_CAT)


This works because the part that is broken is when energy is over 50 so by removing the function at that point it takes it out. However for some of the more complex things it does make it so we have to duplicate a lot of code. Originally the energy test was part of the function, but I had to remove it to remove the bug. Hope this helps you narrow it down.

Honored
Posts: 188
Joined: Tue Dec 14, 2010 5:34 pm

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby Jeshu » Sun Jul 07, 2013 12:47 am

I accidentally pushed out a new release of Ovale instead of a beta. If you're interested, please download 5.3.3 and let me know if there are any bugs in the new features. I have been testing changes on a balance druid and brewmaster monk thus far.

Honored
User avatar
Posts: 72
Joined: Mon Feb 21, 2011 8:56 pm

Re: Leafkiller's MoP Feral/Guardian Ovale Script

Postby scorpio0920 » Sun Jul 07, 2013 2:12 am

5.3.3 error

Spoiler: show
17x Ovale-5.3.3\OvaleCondition.lua:2043: attempt to call global "GetLastSpellInfo" (a nil value)
Ovale-5.3.3\OvaleCondition.lua:2043: in function "condition"
Ovale-5.3.3\OvaleBestAction.lua:293: in function <Ovale\OvaleBestAction.lua:216>
(tail call): ?
Ovale-5.3.3\OvaleBestAction.lua:439: in function <Ovale\OvaleBestAction.lua:437>
(tail call): ?
(tail call): ?
(tail call): ?
Ovale-5.3.3\OvaleBestAction.lua:440: in function <Ovale\OvaleBestAction.lua:437>
(tail call): ?
(tail call): ?
(tail call): ?
Ovale-5.3.3\OvaleBestAction.lua:125: in function <Ovale\OvaleBestAction.lua:111>
OvaleBestAction.lua:125: in function <Ovale\OvaleBestAction.lua:111>
(tail call): ?
Ovale-5.3.3\OvaleBestAction.lua:323: in function <Ovale\OvaleBestAction.lua:312>
(tail call): ?
Ovale-5.3.3\OvaleBestAction.lua:125: in function <Ovale\OvaleBestAction.lua:111>
(tail call): ?
Ovale-5.3.3\OvaleBestAction.lua:323: in function <Ovale\OvaleBestAction.lua:312>
(tail call): ?
Ovale-5.3.3\OvaleFrame.lua:184: in function "OnUpdate"
Ovale-5.3.3\OvaleFrame.lua:82: in function <Ovale\OvaleFrame.lua:81>

Locals:
element = <table> {
nodeId = 10
type = "function"
func = "lastspellestimateddamage"
params = <table> {}
}
self = <table> {
GetActionInfo = <func> @..\OvaleBestAction.lua:700
Compute = <func> @..\OvaleBestAction.lua:809
ComputeBool = <func> @..\OvaleBestAction.lua:823
StartNewAction = <func> @..\OvaleBestAction.lua:695
}
condition = <func> @..\OvaleCondition.lua:2040
OvaleBestAction = <table> {
GetActionInfo = <func> @..\OvaleBestAction.lua:700
Compute = <func> @..\OvaleBestAction.lua:809
ComputeBool = <func> @..\OvaleBestAction.lua:823
StartNewAction = <func> @..\OvaleBestAction.lua:695
}
Ovale = <table> {
SetDefaultModuleLibraries = <func> @Ace3\..\AceAddon-3.0.lua:398
OvalePaperDoll = <table> {}
OvaleState = <table> {}
EnableModule = <func> @Ace3\..\AceAddon-3.0.lua:363
modules = <table> {}
OvaleEquipement = <table> {}
IterateEmbeds = <func> @Ace3\..\AceAddon-3.0.lua:473
casesACocher = <table> {}
Printf = <func> @Ace3\..\AceConsole-3.0.lua:69
PLAYER_REGEN_DISABLED = <func> @..\Ovale.lua:155
now = 81500.645
IsEnabled = <func> @Ace3\..\AceAddon-3.0.lua:482
DisableModule = <func> @Ace3\..\AceAddon-3.0.lua:381
UpdateVisibility = <func> @..\Ovale.lua:173
Log = <func> @..\Ovale.lua:344
OvaleData = <table> {}
RegisterMessage = <func> @Ace3\..\CallbackHandler-1.0.lua:118
UnregisterMessage = <func> @Ace3\..\CallbackHandler-1.0.lua:181
ToggleCheckBox = <func> @..\Ovale.lua:263
OvaleActionBar = <table> {}
dropDowns = <table> {}
Logf = <func> @..\Ovale.lua:350
OvaleSwing = <table> {}
SetEnabledState = <func> @Ace3\..\AceAddon-3.0.lua:455
enabledState = true
RegisterEvent = <func> @Ace3\..\CallbackHandler-1.0.lua:118
OvaleRecount = <table> {}
OvaleFuture = <table> {}
traced = false
checkBoxes = <table> {}
FormatPrint = <func> @..\Ovale.lua:316
frame = <table> {}
IsChecked = <func> @..\Ovale.lua:244
OvaleEnemies = <table> {}
score = 0
Enable = <func> @Ace3\..\AceAddon-3.0.lua:325
listes = <table> {}
DebugPrint = <func> @..\Ovale.lua:320
baseName = "Ovale"
UnregisterDamageMeter = <func> @..\Ovale.lua:298
GetModule = <func> @Ace3\..\AceAddon-3.0.lua:241
SendMessage = <func> @Ace3\..\CallbackHandler-1.0.lua:87
OvaleScripts = <table> {}
OvaleStance = <table> {}
defaultModuleLibraries = <table> {}
OvaleOptions = <table> {}
OvaleCondition = <table> {}
UnregisterChatCommand = <func> @Ace3\..\AceConsole-3.0.lua:111
ToggleOptions = <func> @..\Ovale.lua:169
CHAT_MSG_ADDON = <func> @..\Ovale.lua:130
enCombat = true
UpdateFrame = <func> @..\Ovale.lua:199
OvaleSkada = <table> {}
GetName = <func> @Ace3\..\AceAddon-3.0.lua:310
OvalePool = <table> {}
OvaleComboPoints = <table> {}
OvaleQueue = <table> {}
name = "Ovale"
OvaleSpellDamage = <table> {}
OvaleBestActio

PreviousNext

Return to Kitty DPS

Who is online

Users browsing this forum: Google Adsense [Bot], scorpio0920, Yahoo [Bot] and 6 guests