diff --git a/.gitignore b/.gitignore
index dd378f31da..33e6ef8c83 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,4 +33,5 @@ Settings/*
Unlockers/mmb.lua
Unlockers/daemonic.lua
Unlockers/icc.lua
-Rotations/Rogue/Outlaw/OutlawRavens\.lua
\ No newline at end of file
+Rotations/Rogue/Outlaw/OutlawRavens\.lua
+Deprecated/*
\ No newline at end of file
diff --git a/3rd Party Files/NoName/_BRLoader.lua b/3rd Party Files/NoName/_BRLoader.lua
index eb421c193d..e0caad9068 100644
--- a/3rd Party Files/NoName/_BRLoader.lua
+++ b/3rd Party Files/NoName/_BRLoader.lua
@@ -1,4 +1,5 @@
local Nn = ...
+
-- if --[[(...).name ~= "Nn" or]] Nn == nil then return end
-- local read = Nn.Utils.Storage.read
-- local write = Nn.Utils.Storage.write
@@ -6,9 +7,19 @@ local Nn = ...
-- local AceGUI = Nn.Utils.AceGUI
--Only load BR if in retail 10.2.7 (UI Update); Classic/Cata Not Supported.
-if select(4,GetBuildInfo()) < 100207 then return end
+-- if select(4,GetBuildInfo()) < 100207 then return end
+
+local toc = ""
+local tocPath = '/scripts/BadRotations/BadRotations.toc'
+local FileExists = _G["FileExists"]
+local ReadFile = _G["ReadFile"]
+if not FileExists(tocPath) then print(tocPath, 'does not exist') return end
+if FileExists(tocPath) then
+ toc = ReadFile(tocPath)
+else
+ print('TOC file does not exist:', tocPath)
+end
-local toc = ReadFile('/scripts/BadRotations/BadRotations.toc')
local br = {}
br.files = {}
@@ -51,46 +62,50 @@ br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalStyle-1.0/Media/UbuntuM
br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalStyle-1.0/Media/UbuntuMono-R.ttf', load = true}
-- Add Lua Files from .toc
-for line in toc:gmatch("([^\n]*)\n?") do
- local thisLine = line:trim()
- if thisLine:find('#') == nil and thisLine:len() > 0
- and thisLine:find('.lua') ~= nil
- then
- thisLine = thisLine:gsub("\\","/")
- thisLine = thisLine:gsub(" ","")
- thisLine = thisLine:gsub(".lua","")
- thisLine = 'BadRotations/'..thisLine
- br.files[#br.files+1] = {
- file = thisLine,
- load = true
- }
- -- Lua Files loaded via XML Files
- if thisLine == "BadRotations/Libs/DiesalStyle-1.0/DiesalStyle-1.0" then
- br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/DiesalGUI-1.0', load = true}
- br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Window', load = true}
- br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/ScrollFrame', load = true}
- br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/ScrollingEditBox', load = true}
- br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/ScrollingMessageFrame', load = true}
- br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/CheckBox', load = true}
- br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Button', load = true}
- br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Spinner', load = true}
- br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Input', load = true}
- br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Dropdown', load = true}
- br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/DropdownItem', load = true}
- br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/ComboBox', load = true}
- br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/ComboBoxItem', load = true}
- br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Accordian', load = true}
- br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/AccordianSection', load = true}
- br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Tree', load = true}
- br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Branch', load = true}
- br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Bar', load = true}
- br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalMenu-1.0/DiesalMenu-1.0', load = true}
- br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalMenu-1.0/Objects/MenuItem', load = true}
- br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalMenu-1.0/Objects/Menu', load = true}
+function LoadFilesFromTOC(tocFile)
+ for line in tocFile:gmatch("([^\n]*)\n?") do
+ local thisLine = line:trim()
+ if thisLine:find('#') == nil and thisLine:len() > 0
+ and thisLine:find('.lua') ~= nil
+ then
+ thisLine = thisLine:gsub("\\","/")
+ thisLine = thisLine:gsub(" ","")
+ thisLine = thisLine:gsub(".lua","")
+ thisLine = 'BadRotations/'..thisLine
+ br.files[#br.files+1] = {
+ file = thisLine,
+ load = true
+ }
+ -- Lua Files loaded via XML Files
+ if thisLine == "BadRotations/Libs/DiesalStyle-1.0/DiesalStyle-1.0" then
+ br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/DiesalGUI-1.0', load = true}
+ br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Window', load = true}
+ br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/ScrollFrame', load = true}
+ br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/ScrollingEditBox', load = true}
+ br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/ScrollingMessageFrame', load = true}
+ br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/CheckBox', load = true}
+ br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Button', load = true}
+ br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Spinner', load = true}
+ br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Input', load = true}
+ br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Dropdown', load = true}
+ br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/DropdownItem', load = true}
+ br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/ComboBox', load = true}
+ br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/ComboBoxItem', load = true}
+ br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Accordian', load = true}
+ br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/AccordianSection', load = true}
+ br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Tree', load = true}
+ br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Branch', load = true}
+ br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Bar', load = true}
+ br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalMenu-1.0/DiesalMenu-1.0', load = true}
+ br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalMenu-1.0/Objects/MenuItem', load = true}
+ br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalMenu-1.0/Objects/Menu', load = true}
+ end
end
end
end
+LoadFilesFromTOC(toc)
+
-- Unlocker Lua Files
br.files[#br.files+1] = {file = 'BadRotations/Unlockers/tinkr', load = false}
br.files[#br.files+1] = {file = 'BadRotations/Unlockers/nn', load = true}
@@ -109,4 +124,4 @@ end
if UnitExists('player') then
print "[Nn] Loading BR"
br.load()
-end
+end
\ No newline at end of file
diff --git a/BR2-ROADMAP.md b/BR2-ROADMAP.md
deleted file mode 100644
index 7a68124cd0..0000000000
--- a/BR2-ROADMAP.md
+++ /dev/null
@@ -1,69 +0,0 @@
-# ROADMAP
-
-## Code Standards
-### Files
-1. **FILES** should be descriptive of the functionality. Examples: BRDebug contains functions related to debugging like print to chat, output to debug window, maybe logging to file. If the code being developed relates to any form of debug logging it must exist within the BRDebug file.
-
-### Code Annotation
-1. Each module should be specific to a single type of functionality and be defined as a class using ---@class
-2. Classes need to be initialized using the BRLoader:CreateModule([ModuleName]) method and be retrieved using the BRLoader:LoadModule([ModuleName]) method.
- Example:
-
-```
-local _,br,_ = ...
-
----@class BRDebug
-local BRDebug = BRLoader:CreateModule("BRDebug")
-
----@type BRConstants
-local BRConstants = BRLoader:LoadModule("BRConstants")
-
----@param message string @Message to log
----@return nil
-function BRDebug:Log(message)
- br._G.print(BRConstants.colors.purple .. "[BR2] " .. BRConstants.colors.white .. message .. "|r")
-end
-```
-
-3. Each method inside of the class must be annotated with ---@params, etc. See documentation here: https://github.com/LuaLS/lua-language-server/wiki/Annotations
-
-### Environment
-1. Each file has three enivonment objects passed to which should be retrieved at the beginning of the file. These are assigned during injection. The developer should primarily be concerned with the second argument which is the global br object. The third is access directly to the unlocker but given the agnostic approach of all modules this should not be used within the core code. Access to the unlocker should only be made in code within the "init' directory. This is currently in play only during initial development and **WILL BE REMOVED** before general consumption.
-```
-local _,br,_ = ...
-```
-
-### Naming
-3. "**br**" remains the global add-on variable
-4. **Functions** should be named with PascalCase. That is fully capitilized full words, example: RunSomethingReallyCool()
-5. **Variables** should be named with CamelCase. That is a lower case inital word followed by capitalized words. example: br.someStorageVariable = nil
-
-## Overall Goals
-1. Make system compatible with classic, Cataclysm, and retail without separate builds or branches
-2. Focus on human style rotation logic. This will include randomized pulses, facing of targets, dynamic targeting, etc.
-3. Support the three current Unlockers, Nil-Name, Tinkr, and Daemonic (if it ever resurfaces, currently not supported)
-4. Focus on rotations only. Don't port over any code that doesn't related to Damage, tank, or healing rotations. (i.e. no fishing, quest, movement, etc.) This may change in the future but the first pass will attempt to reduce the addOn's footprint and reduce latency.
-5. Remove dependency on manually reading the WOW table of contents file and instead handle injection of code in unlocker specific files. Unlockers that do not support injecting code will not be supported.
-6. Change settings files to JSON. All current unlockers support JSON code/decode.
-
-## Future Nice-To-Haves
-1. UI Improvements: Object Browser, Object Highlighting/lines, Damage/DPS meter/graph,
-2. UI Improvements: wider configuration to avoid clipping
-3. HTTP Request : Possibly allow loading of routine from a location
-
-## Current Progress
-- [x] Remove TOC
-- [x] Move injection of files to unlocker specific code, minimize _BRLoader.lua code to three or four lines, just enough to spawn project unlocker initialization.
-- [x] Specific unlocker loaders, configurations
-- [x] Implement NilName Unlocker for initial development
-- [x] Implement specific loading of files based on client version (Classic, Cata, Retail)
-- [x] Implement Object Manager copy
-- [ ] Re-do file injection to support pre/unlocked post/unlocked category
-- [ ] Create TOC files, including per-client TOCs, maybe use scripting to build this so it's always up to date.
-- [ ] Implement Object Browser for development
-- [ ] Implement Settings class to handle global and rotation specific settings in JSON files
-- [ ] Implement Object Filter for Units, and Unit tracking
-- [ ] Implement Aura tracking
-- [ ] Implement Spell system maintaining separation of client versions
-
-
diff --git a/BadRotations API.md b/BadRotations API.md
index 4c16ac56db..1748ade55a 100644
--- a/BadRotations API.md
+++ b/BadRotations API.md
@@ -13,54 +13,54 @@
---
-`br.UnitBuffID(UnitID, SpellID, Filter) - 1/nil` Returns 1 if Buff found on target
-`br.UnitDebuffID(UnitID, SpellID, Filter) - 1/nil` Returns 1 if Debuff found on target
+`br.functions.aura:UnitBuffID(UnitID, SpellID, Filter) - 1/nil` Returns 1 if Buff found on target
+`br.functions.aura:UnitDebuffID(UnitID, SpellID, Filter) - 1/nil` Returns 1 if Debuff found on target
**CAN - Booleans**
---
`canAttack(Unit1,Unit2) - Bool` Returns true if Unit1 can attack Unit2
-`br.canDispel(Unit,spellID) - Bool` Retruns true if the Unit have a valid Dispel
+`br.functions.aura:canDispel(Unit,spellID) - Bool` Retruns true if the Unit have a valid Dispel
`canHeal(Unit) - Bool` Returns true if we can heal that Unit
-`br.canInterrupt(spellID,percentint) - Bool` Returns true if we can Interrupt that target
+`br.functions.spell:canInterrupt(spellID,percentint) - Bool` Returns true if we can Interrupt that target
`canPrepare() - Bool` Returns true if ready to buff(ooc)
-`br.canRun() - Bool` Returns true if we can Run profile(incombat)
-`br.canUseItem(itemID) - Bool` Returns true if the item can be used
+`br.functions.action:canRun() - Bool` Returns true if we can Run profile(incombat)
+`br.functions.item:canUseItem(itemID) - Bool` Returns true if the item can be used
**CAST - Function Methods**
---
`castGround(Unit,SpellID,maxDistance)` Used to cast Spells on ground. Returns true if everything passes.
`castGroundBetween(Unit,SpellID,maxDistance) ` Used to ground cast between yourself and target. Returns true if everything passes.
-`castHealGround(SpellID,Radius,Health,NumberOfPlayers)` Used to ground cast between lowest br.friend members. Returns true if everything passes.
+`castHealGround(SpellID,Radius,Health,NumberOfPlayers)` Used to ground cast between lowest br.engines.healingEngine.friend members. Returns true if everything passes.
[castSpell(Unit,SpellID,FacingCheck,MovementCheck,SpamAllowed,KnownSkip)](https://github.com/CuteOne/BadRotations/wiki/castSpell-Method#castspell) Used to cast Spells. Returns true if everything passes.
**GET - Mixed**
---
`getAllies(Target,Radius) - Table` Returns a table of the allies found within Radius of the target
-`br.getBuffRemain(Unit,BuffID) - Num` Returns how long remain on this Unit buff.
-`br.getBuffStacks(Unit,BuffID) - Num` Returns number of stacks of this Units buff.
-`br.getCombatTime() - Num` Returns time since combat started.
+`br.functions.aura:getBuffRemain(Unit,BuffID) - Num` Returns how long remain on this Unit buff.
+`br.functions.aura:getBuffStacks(Unit,BuffID) - Num` Returns number of stacks of this Units buff.
+`br.functions.combat:getCombatTime() - Num` Returns time since combat started.
`getCreatureType(Unit) - Bool` Returns true if Unit is not a pet battle or a totem.
`getCombo() - Num` Returns combo points on current target.
-`br.getDebuffRemain(Unit,DebuffID) - Num` Returns how long remain on this Units Debuff.
-`br.getDebuffStacks(Unit,DebuffID) - Num` Returns number of stacks of this Units Debuff.
-`br.getDistance(Unit1,Unit2) - Num` Returns distance to target in yards.
-`br.getEnemies(Target,Radius) - Table` Returns a table of the Enemies found within Radius of the target
-`br.getFacing(Unit1,Unit2) - Bool` Returns true if Unit1 is facing Unit2
+`br.functions.aura:getDebuffRemain(Unit,DebuffID) - Num` Returns how long remain on this Units Debuff.
+`br.functions.aura:getDebuffStacks(Unit,DebuffID) - Num` Returns number of stacks of this Units Debuff.
+`br.functions.range:getDistance(Unit1,Unit2) - Num` Returns distance to target in yards.
+`br.engines.enemiesEngineFunctions:getEnemies(Target,Radius) - Table` Returns a table of the Enemies found within Radius of the target
+`br.functions.unit:getFacing(Unit1,Unit2) - Bool` Returns true if Unit1 is facing Unit2
`getFallTime() - Num` Returns the time the character has been falling.
`getGround(Unit) - Bool` Returns true if ground is found under the target
-`br.getHP(Unit) - Num` Returns Unit HP %
-`br.getLineOfSight(Unit1,Unit2) - Bool` Returns true if the Unit1 can see Unit2
-`getLowAllies(Value) - Num` Returns number of units under Value HP in br.friend
+`br.functions.unit:getHP(Unit) - Num` Returns Unit HP %
+`br.functions.misc:getLineOfSight(Unit1,Unit2) - Bool` Returns true if the Unit1 can see Unit2
+`getLowAllies(Value) - Num` Returns number of units under Value HP in br.engines.healingEngine.friend
`getMana(Unit) - Num` Returns Unit Mana %
`getNumEnemies(Target,Radius) - Num` Returns number of Enemies found within Radius of the target
`getPetLineOfSight(Unit) - Bool` Returns true if our pet is in sight of target
-`br.getPower(Unit) - Num` Returns Unit Power %
-`br.getRegen(Unit) - Num` Returns power regen rate of Unit
+`br.functions.power:getPower(Unit) - Num` Returns Unit Power %
+`br.functions.power:getRegen(Unit) - Num` Returns power regen rate of Unit
`round2(num, idp) - Num` Used to round numbers
-`br.getSpellCD(SpellID) - Num` Returns how long remain until the CD is ready
+`br.functions.spell:getSpellCD(SpellID) - Num` Returns how long remain until the CD is ready
`getTimeToDie(unit) - Num` Returns approximative Time To Die for Unit
`getTimeToMax(Unit) - Num` Returns how long it will take until our ressources are maxed
`getTotemDistance(Unit1) - Num` Returns distance from totem to target
@@ -74,18 +74,18 @@
---
-`br.isAlive(Unit) - Bool` Returns true if Unit is alive
-`br.isBoss() - Bool` Returns true if a boss is found in boss 1-2-3-4-5
+`br.functions.unit:isAlive(Unit) - Bool` Returns true if Unit is alive
+`br.functions.unit:isBoss() - Bool` Returns true if a boss is found in boss 1-2-3-4-5
`isBuffed(UnitID,SpellID,TimeLeft) - Bool` Rturns true if Unit have at least Timeleft remaining on SpellId Buff
`isCasting(SpellID,Unit) - Bool` Returns true if Unit is casting given spell
-`br.isCastingSpell(spellID) - Bool` returns true if WE are casting spellid
-`br.isDummy(Unit) - Bool` Returns true if Unit is a dummy
+`br.functions.cast:isCastingSpell(spellID) - Bool` returns true if WE are casting spellid
+`br.functions.unit:isDummy(Unit) - Bool` Returns true if Unit is a dummy
`isEnnemy(Unit) - Bool` Returns true if we can attack the Unit
`isGarrMCd(Unit) - Bool` Returns true if Unit if affected by Garrosh MC
`isInCombat(Unit) - Bool` Returns true if Unit is in combat
`isInMelee(Unit) - Bool` Returns true if we are withing 4 yard of the Unit
`isInPvP() - Bool` Returns true if we are in PvP
-`br.isKnown(spellID) - Bool` Returns true if we know this spell(via spellbook check)
+`br.functions.spell:isKnown(spellID) - Bool` Returns true if we know this spell(via spellbook check)
`isLooting() - Bool` Returns true if we are currently looting
`isMoving(Unit) - Num` Returns Unit movement speed
`IsMovingTime(time) - Bool` Returns true if we have been moving for time seconds
@@ -95,19 +95,19 @@
---
-`makeEnemiesTable()` - does not return anything but create br.enemy that hold br.enemy[i].unit br.enemy[i].distance and br.enemy[i].hp of all enemies in 40 yards
+`makeEnemiesTable()` - does not return anything but create br.engines.enemiesEngine.enemy that hold br.engines.enemiesEngine.enemy[i].unit br.engines.enemiesEngine.enemy[i].distance and br.engines.enemiesEngine.enemy[i].hp of all enemies in 40 yards
`nDbDmg(tar, spellID, player) - Num` Returns tooltip damage
`pause() - Bool` Returns true if pause is engaged
`castingUnit() - Bool` Returns true is said unit is casting.
-`br.useItem(itemID)` Use item via ID
+`br.functions.item:useItem(itemID)` Use item via ID
`shouldStopCasting(SpellID) - Bool` Built into casts methods. Prevents spell locking.
**Config Queries - Mixed**
---
-`br.isChecked(Value) - Bool` Returns true if Value Checkbox is checked in UI
+`br.functions.misc:isChecked(Value) - Bool` Returns true if Value Checkbox is checked in UI
`isSelected(Value) - Bool` Returns true if Value Checkbox is checked in UI and CD requirements are met.
-`br.getValue(Value) - Num` Returns drop or box Value from UI
+`br.functions.misc:getValue(Value) - Num` Returns drop or box Value from UI
`CreateNewCheck(value, textString, tip1, state)`
- Used to create checkboxes in UI
diff --git a/BadRotations.lua b/BadRotations.lua
index 80b17baf23..f295b1fd69 100644
--- a/BadRotations.lua
+++ b/BadRotations.lua
@@ -1,208 +1,90 @@
-- [[ Global Variables and Tables Initialization ]] --
-- define br global that will hold the bot global background features
local _, br = ...
+
+local loadedIn = false
+-- Guard to make br.load idempotent when multiple loaders invoke it
+local _br_loaded = false
br._G = setmetatable({}, { __index = _G })
-br._G.GetSpellInfo = function(spellIdentifier)
- local spellInfo = br._G.C_Spell.GetSpellInfo(spellIdentifier)
- if spellInfo then
- ---@diagnostic disable-next-line: redundant-return-value
- return spellInfo.name, _, spellInfo.iconID, spellInfo.castTime, spellInfo.minRange,
- ---@diagnostic disable-next-line: redundant-return-value
- spellInfo.maxRange, spellInfo.spellID, spellInfo.originalIconID
- end
-end
-br.unlock = {}
-br.data = {}
-br.data.settings = {}
-br.addonName = "BadRotations"
-br.auraMaxStacks = {}
-br.castPosition = { x = 0, y = 0, z = 0 }
-br.commandHelp = {}
-br.deadPet = false
--- developers debug, use /run br.data.settings[br.selectedSpec].toggles["isDebugging"] = true
-br.debug = {}
-br.dropOptions = {}
---br.dropOptions.Toggle = {"LeftCtrl","LeftShift","RightCtrl","RightShift","RightAlt","None"}
-br.dropOptions.Toggle = {
- "LeftCtrl",
- "LeftShift",
- "RightCtrl",
- "RightShift",
- "RightAlt",
- "None",
- "MMouse",
- "Mouse4",
- "Mouse5"
-}
-br.dropOptions.CD = { "Never", "CDs", "Always" }
-br.engines = {}
-br.loadedIn = false
-br.loadFile = false
-br.pauseCast = br._G.GetTime()
-br.prevQueueWindow = br._G.GetCVar("SpellQueueWindow")
-br.profile = {}
-br.rotations = {}
-br.selectedSpec = "None"
-br.selectedSpecID = 0
-br.selectedProfile = 1
-br.selectedProfileName = "None"
-br.settingsDir = "\\"
-br.settingsFile = "None.lua"
-br.unlocker = "None"
--- [[ Class Colors Definition ]] --
--- The colors Duke, the colors!
-br.classColors = {
- [1] = { class = "Warrior", B = 0.43, G = 0.61, R = 0.78, hex = "c79c6e" },
- [2] = { class = "Paladin", B = 0.73, G = 0.55, R = 0.96, hex = "f58cba" },
- [3] = { class = "Hunter", B = 0.45, G = 0.83, R = 0.67, hex = "abd473" },
- [4] = { class = "Rogue", B = 0.41, G = 0.96, R = 1, hex = "fff569" },
- [5] = { class = "Priest", B = 1, G = 1, R = 1, hex = "ffffff" },
- [6] = { class = "Deathknight", B = 0.23, G = 0.12, R = 0.77, hex = "c41f3b" },
- [7] = { class = "Shaman", B = 0.87, G = 0.44, R = 0, hex = "0070de" },
- [8] = { class = "Mage", B = 0.94, G = 0.8, R = 0.41, hex = "69ccf0" },
- [9] = { class = "Warlock", B = 0.79, G = 0.51, R = 0.58, hex = "9482c9" },
- [10] = { class = "Monk", B = 0.59, G = 1, R = 0, hex = "00ff96" },
- [11] = { class = "Druid", B = 0.04, G = 0.49, R = 1, hex = "ff7d0a" },
- [12] = { class = "Demonhunter", B = 0.79, G = 0.19, R = 0.64, hex = "a330c9" },
- [13] = { class = "Evoker", B = 0.50, G = 0.58, R = 0.20, hex = "33937f" }
-}
-br.classColor = tostring("|cff" .. br.classColors[select(3, br._G.UnitClass("player"))].hex)
-br.qualityColors = {
- blue = "0070dd",
- green = "1eff00",
- white = "ffffff",
- grey = "9d9d9d"
-}
-br.druid = {}
-br.evoker = {}
+-- WoW API Compatibility Layer
+-- Expansion-specific API wrappers are loaded via wowapi.lua -> retail.lua or mop.lua
+-- This allows all API differences to be managed in one place per expansion
+-- Access API functions via: br.api.wow.FunctionName() or br._G.FunctionName()
-local nameSet = false
--- [[ Functions Definition ]] --
-function br.setAddonName()
- if not nameSet then
- nameSet = true
+-- Override br._G to use compatibility layer when available
+local originalIndex = getmetatable(br._G).__index
+setmetatable(br._G, {
+ __index = function(t, k)
+ -- First check if we have a compatibility wrapper
+ if br.api and br.api.wow and br.api.wow[k] then
+ return br.api.wow[k]
+ end
+ -- Fall back to global _G
+ return originalIndex[k]
end
-end
+})
+-- System Table Initialization
+br.api = br.api or {} -- Don't overwrite if already initialized by wowapi.lua
+br.engines = {}
+br.functions = {}
+br.lists = {}
+br.loader = {}
+br.misc = {}
+br.readers = {}
+br.ui = {}
+br.debug = {}
+br.slashCommands = {}
+br.unlockers = {}
--- Custom Print
-function br.debugPrint(message)
- if br.data.settings[br.selectedSpec].toggles["isDebugging"] == true then
- br._G.print(message)
- end
-end
+-- Other Initialization
+br.data = {}
+br.data.settings = {}
+br.loader.addonName = "BadRotations"
+br.loader.rotations = {}
+br.loader.selectedSpec = "None"
+br.loader.selectedSpecID = 0
+br.loader.selectedProfile = 1
+br.loader.selectedProfileName = "None"
+br.loader.settingsFile = "None.lua"
+-- developers debug, use /run br.data.settings[br.loader.selectedSpec].toggles["isDebugging"] = true
-- Run
function br.Run()
- if br.selectedSpec == nil then
- br.selectedSpecID, br.selectedSpec = br._G.GetSpecializationInfo(br._G.GetSpecialization())
- if br.selectedSpec == "" then
- br.selectedSpec = "Initial"
+ if br.loader.selectedSpec == nil then
+ br.loader.selectedSpecID, br.loader.selectedSpec = br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization())
+ if br.loader.selectedSpec == "" then
+ br.loader.selectedSpec = "Initial"
end
end
-- add minimap fire icon
- br:MinimapButton()
+ br.ui:MinimapButton()
-- Build up pulse frame (hearth)
- if not br.loadedIn then
+ if not loadedIn then
-- Start Logs
-- combat log
- br.read.combatLog()
+ br.readers.combatLog:combatLog()
-- other readers
- br.read.commonReaders()
+ br.readers.common:commonReaders()
-- Start Engines
- br:Engine()
- br:ObjectManager()
- br:ObjectTracker()
- -- Complete Loadin
- br.ChatOverlay("-= BadRotations Loaded =-")
- br._G.print("Initialization Complete, Finding Previous Settings.")
- br.loadedIn = true
- end
-end
-
--- Default Settings
-function br.defaultSettings()
- br._G.C_Timer.After(
- 2,
- function()
- if br.ui.window.config == nil then
- br.ui.window.config = {}
- end
- if br.ui.window.config.frame == nil then
- br.ui:createConfigWindow()
- end
- br.ui:toggleWindow("config")
- br.ui:closeWindow("config")
- end
- )
- -- Settings Per Spec
- if br.data.settings[br.selectedSpec].toggles == nil then
- br.data.settings[br.selectedSpec].toggles = {}
- end
- if br.data.settings[br.selectedSpec]["RotationDrop"] == nil then
- br.selectedProfile = 1
- else
- br.selectedProfile = br.data.settings[br.selectedSpec]["RotationDrop"]
- end
- if br.data.settings[br.selectedSpec][br.selectedProfile] == nil then
- br.data.settings[br.selectedSpec][br.selectedProfile] = {}
- end
- -- Define Main Button if no settings exist
- if (br.data.settings and br.data.settings.mainButton == nil) then
- br.data.settings.mainButton = {
- pos = {
- anchor = "CENTER",
- x = -75,
- y = -200
- }
- }
- br.data.settings.buttonSize = 32
- br.data.settings.font = "Fonts/arialn.ttf"
- br.data.settings.fontsize = 16
- br.data.settings.wiped = true
- end
- -- Define Minimap Button if no settings exist
- if (br.data.settings and br.data.settings.minimapButton == nil) then
- br.data.settings.minimapButton = {
- pos = {
- x = 75.70,
- y = -6.63
- }
- }
- end
-end
-
--- Load Saved Settings
-function br.loadSavedSettings()
- if br.initializeSettings then
- br.initOM = true
- br.loader.loadProfiles()
- br:loadLastProfileTracker()
- if br.data.settings[br.selectedSpec]["RotationDropValue"] then
- br:loadSettings(nil, nil, nil, br.data.settings[br.selectedSpec]["RotationDropValue"])
- elseif br.rotations[br.selectedSpec] then
- br:loadSettings(nil, nil, nil, br.rotations[br.selectedSpec][1].name)
- else
- if not br.rotations[br.selectedSpec] then
- br.initializeSettings = false
- return
- end
+ br.engines:Main()
+ br.engines:ObjectManager()
+ br.engines:ObjectTracker()
+ -- Initialize Loot Engine
+ if br.engines.lootEngine and br.engines.lootEngine.init then
+ br.engines.lootEngine:init()
end
- br.defaultSettings()
- -- Build the Toggles
- br.TogglesFrame()
- -- Restore Minimap Button Position
- br.BadRotationsButton:SetPoint("CENTER", br.data.settings.minimapButton.pos.x,
- br.data.settings.minimapButton.pos.y)
- br.initializeSettings = false
+ loadedIn = true
end
end
function br.load()
+ if _br_loaded then return end
+ _br_loaded = true
-- Update Selected Spec
- br.selectedSpecID, br.selectedSpec = br._G.GetSpecializationInfo(br._G.GetSpecialization())
- if br.selectedSpec == "" then
- br.selectedSpec = "Initial"
+ br.loader.selectedSpecID, br.loader.selectedSpec = br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization())
+ if br.loader.selectedSpec == "" then
+ br.loader.selectedSpec = "Initial"
end
if br.data == nil then
br.data = {}
@@ -216,22 +98,20 @@ function br.load()
if br.data.ui == nil then
br.data.ui = {}
end
- if br.data.settings[br.selectedSpec] == nil then
- br.data.settings[br.selectedSpec] = {}
+ if br.data.settings[br.loader.selectedSpec] == nil then
+ br.data.settings[br.loader.selectedSpec] = {}
end
if not br.unlocked then
- br.initializeSettings = true
- print(br.classColor .. "[BadRotations] |cffFFFFFFInitializing Please Wait...")
+ br.ui.settingsManagement.initializeSettings = true
+ print(br.ui.colors.class .. "[BadRotations] |cffFFFFFFInitializing Please Wait...")
end
- br.equipHasChanged = true
- if not br.loadedIn then
- if br.damaged == nil then
- br.damaged = {}
+ if not loadedIn then
+ if br.engines.enemiesEngine.damaged == nil then
+ br.engines.enemiesEngine.damaged = {}
end
- br.bagsUpdated = true
+ -- br.bagsUpdated = true
br:Run()
end
- br.timeOfLastLoadingScreen = br._G.GetTime()
end
-- [[ Event Listeners ]] --
@@ -252,23 +132,29 @@ local function OnEvent(self, event)
br.disablePulse = false
end
if event == "PLAYER_LOGOUT" then
- if br.unlocked then
- -- Return queue window to previous setting
+ -- Attempt to save settings and UI state on logout regardless of unlock status.
+ -- Guard calls to avoid errors if subsystems are not available.
+ -- Return queue window to previous setting if possible
+ if br._G and br._G.C_CVar and br._G.GetCVar and br._G.RunMacroText then
if br._G.C_CVar.GetCVar("SpellQueueWindow") == "0" then
- br._G.RunMacroText("/console SpellQueueWindow " .. br.prevQueueWindow)
+ br._G.RunMacroText("/console SpellQueueWindow " .. br._G.GetCVar("SpellQueueWindow"))
end
+ end
+ if br.ui and br.ui.settingsManagement then
br.ui:saveWindowPosition()
- br:cleanSettings()
- table.wipe(br.data.settings[br.selectedSpec][br.selectedProfile]["PageList"])
- table.wipe(br.ui)
- if br.getOptionCheck("Reset Options") then
+ br.ui.settingsManagement:cleanSettings()
+ if br.data and br.data.settings and br.data.settings[br.loader.selectedSpec] and br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile] and br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["PageList"] then
+ table.wipe(br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["PageList"])
+ end
+ if br.functions and br.functions.misc and br.functions.misc:getOptionCheck("Reset Options") then
-- Reset Settings
- br:saveSettings(nil, nil, br.selectedSpec, br.selectedProfileName, true)
+ br.ui.settingsManagement:saveSettings(nil, nil, br.loader.selectedSpec, br.loader.selectedProfileName, true)
else
-- Save Settings
- br:saveSettings(nil, nil, br.selectedSpec, br.selectedProfileName)
+ br.ui.settingsManagement:saveSettings(nil, nil, br.loader.selectedSpec, br.loader.selectedProfileName)
end
- br.saveLastProfileTracker()
+ br.ui.settingsManagement:saveLastProfileTracker()
+ table.wipe(br.ui)
end
end
if event == "PLAYER_ENTERING_WORLD" then
diff --git a/BadRotations.toc b/BadRotations.toc
index 117a9b8be0..816e64a612 100644
--- a/BadRotations.toc
+++ b/BadRotations.toc
@@ -44,6 +44,13 @@ Libs\DiesalLibs.lua
#@end-no-lib-strip@
+## WoW API Compatibility
+System\wowapi.lua
+System\Expansions\retail.lua
+System\Expansions\mop.lua
+System\Expansions\tbc.lua
+System\Expansions\classic.lua
+
BadRotations.lua
System\UI\UI.lua
System\UI\MinimapButton.lua
@@ -58,8 +65,6 @@ misc.xml
System\Unlockers.lua
System\Core.lua
System\Debugging.lua
-System\LootManager.lua
-System\UsefulFeatures.lua
System\SlashCommands.lua
## Functions
@@ -78,10 +83,13 @@ System\Functions\Range.lua
System\Functions\Spell.lua
System\Functions\Unit.lua
System\Functions\SmartQueue.lua
+System\Functions\Glyph.lua
+System\Functions\Totem.lua
## Lists
System\Lists\AoEDamage.lua
+System\Lists\Bosses.lua
System\Lists\BurnUnits.lua
System\Lists\CCUnits.lua
System\Lists\Defensives.lua
@@ -118,12 +126,17 @@ System\Engines\HealingEngineFrame.lua
System\Engines\HealingEngineFunctions.lua
#System\Engines\InterruptsFrame.lua -- Unused
System\Engines\Interrupts.lua
+System\Engines\LootEngine.lua
+System\Engines\LootEngineFunctions.lua
System\Engines\ttdTable.lua
System\Engines\QuestTracker.lua
System\Engines\Tracker.lua
## BadRotations UI
+System\UI\Tooltips.lua
+System\UI\Colors.lua
+System\UI\DropOptions.lua
System\UI\SettingsManagement.lua
System\UI\ChatOverlay.lua
#System\UI\MinimapButton.lua
@@ -160,6 +173,7 @@ System\API\Debuff.lua
System\API\Enemies.lua
System\API\Essence.lua
System\API\Equiped.lua
+System\API\Glyph.lua
System\API\Has.lua
System\API\Item.lua
System\API\Module.lua
@@ -168,6 +182,7 @@ System\API\Power.lua
# System\API\Runeforge.lua
System\API\Spell.lua
System\API\Talent.lua
+System\API\Totem.lua
System\API\UI.lua
System\API\Unit.lua
System\API\Units.lua
@@ -203,8 +218,6 @@ System\Updater.lua
# System\Unlockers.lua
# System\Core.lua
# System\Debugging.lua
-# System\LootManager.lua
-# System\UsefulFeatures.lua
# System\SlashCommands.lua
# ## Functions
@@ -281,12 +294,15 @@ System\Updater.lua
# System\Engines\HealingEngineFunctions.lua
# #System\Engines\InterruptsFrame.lua -- Unused
# System\Engines\Interrupts.lua
+# System\Engines\LootEngine.lua
+# System\Engines\LootEngineFunctions.lua
# System\Engines\ttdTable.lua
# System\Engines\QuestTracker.lua
# System\Engines\Tracker.lua
# ## BadRotations UI
+# System\UI\Tooltips.lua
# System\UI\SettingsManagement.lua
# System\UI\ChatOverlay.lua
# System\UI\MinimapButton.lua
diff --git a/Libs/!LibDraw/LibDraw.lua b/Libs/!LibDraw/LibDraw.lua
index 5961a1d712..7ec597fba0 100644
--- a/Libs/!LibDraw/LibDraw.lua
+++ b/Libs/!LibDraw/LibDraw.lua
@@ -1,12 +1,12 @@
-- LubDraw by docbrown on fh-wow.com
-local _, br = ...
+local Nn, br = ...
local LibDraw
local sin, cos, atan, atan2, sqrt, rad = math.sin, math.cos, math.atan, math.atan2, math.sqrt, math.rad
local tinsert, tremove = tinsert, tremove
local function WorldToScreen (wX, wY, wZ)
- if wZ == nil then wZ = select(3,br.GetObjectPosition("player")) end
+ if wZ == nil then wZ = select(3,br.functions.unit:GetObjectPosition("player")) end
-- for k,v in pairs(br._G) do
-- br._G.print("K: "..tostring(k)..", V: "..tostring(v))
-- end
@@ -14,12 +14,25 @@ local function WorldToScreen (wX, wY, wZ)
if not sX or sX == 0 then
return false, false
end
- if sX and sY and br.unlocker ~= "WA" and br.unlocker ~= "LuaBox" then
- return sX, -(WorldFrame:GetTop() - sY);
- else
+ -- if sX and sY and br.unlockers.selected ~= "WA" and br.unlockers.selected ~= "LuaBox" then
+ -- return sX, -(WorldFrame:GetTop() - sY);
+ -- else
return sX, sY;
- end
+ -- end
end
+-- local UIParent = UIParent
+-- local function WorldToScreen(sx, sy, sz)
+-- local x, y = Nn.WorldToScreen(sx, sy, sz)
+-- if x == 0 and y == 0 then
+-- return
+-- end
+-- local scale = UIParent:GetScale()
+-- local width = UIParent:GetWidth() * scale
+-- local height = UIParent:GetHeight() * scale
+-- x = x * width
+-- y = -(height - (y * height))
+-- return x, y
+-- end
if LibStub then
-- LibStub version control
@@ -135,8 +148,8 @@ function LibDraw.Array(vectors, x, y, z, rotationX, rotationY, rotationZ)
ex, ey, ez = LibDraw.rotateZ(x, y, z, ex, ey, ez, rotationZ)
end
- local sx, sy = WorldToScreen(sx, sy, sz)
- local ex, ey = WorldToScreen(ex, ey, ez)
+ sx, sy = WorldToScreen(sx, sy, sz)
+ ex, ey = WorldToScreen(ex, ey, ez)
LibDraw.Draw2DLine(sx, sy, ex, ey)
end
end
diff --git a/Libs/DiesalGUI-1.0/DiesalGUI-1.0.lua b/Libs/DiesalGUI-1.0/DiesalGUI-1.0.lua
index 96bc3ae86c..761bd8d8c3 100644
--- a/Libs/DiesalGUI-1.0/DiesalGUI-1.0.lua
+++ b/Libs/DiesalGUI-1.0/DiesalGUI-1.0.lua
@@ -120,16 +120,43 @@ end
-- ~~| Object Diesal Base |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ObjectBase.CreateRegion = function(self, regionType, regionName, parentRegion, defaultFontObject)
if regionType == 'FontString' then
- local fontString = parentRegion:CreateFontString()
+ -- Try creating with explicit parameters
+ local fontString = parentRegion:CreateFontString(nil, "OVERLAY")
+
+ -- CRITICAL: Set font IMMEDIATELY and DON'T let anything override it if it fails
+ -- Start with GameFontNormal which is guaranteed to work
+ fontString:SetFontObject(_G.GameFontNormal)
+
-- set Default font properties
if defaultFontObject then
fontString.defaultFontObject = defaultFontObject
else
- fontString.defaultFontObject = DiesalFontNormal
+ fontString.defaultFontObject = _G.DiesalFontNormal or _G.GameFontNormal
+ end
+
+ -- ONLY try to apply custom font if we can verify it's valid first
+ if fontString.defaultFontObject and fontString.defaultFontObject ~= _G.GameFontNormal and type(fontString.defaultFontObject.GetFont) == "function" then
+ local success, fontPath, fontSize, fontFlags = pcall(fontString.defaultFontObject.GetFont, fontString.defaultFontObject)
+ -- Only apply if we got a valid font path
+ if success and fontPath and fontPath ~= "" then
+ local fontSuccess = pcall(fontString.SetFont, fontString, fontPath, fontSize or 12, fontFlags or "")
+ -- If custom font failed, font object fallback is still active from SetFontObject above
+ end
+ end
+
+ -- Set colors and spacing safely
+ if fontString.defaultFontObject and type(fontString.defaultFontObject.GetTextColor) == "function" then
+ local success, r, g, b, a = pcall(fontString.defaultFontObject.GetTextColor, fontString.defaultFontObject)
+ if success and r then
+ pcall(fontString.SetTextColor, fontString, r, g, b, a or 1)
+ end
+ end
+ if fontString.defaultFontObject and type(fontString.defaultFontObject.GetSpacing) == "function" then
+ local success, spacing = pcall(fontString.defaultFontObject.GetSpacing, fontString.defaultFontObject)
+ if success and spacing then
+ pcall(fontString.SetSpacing, fontString, spacing)
+ end
end
- fontString:SetFont(fontString.defaultFontObject:GetFont())
- fontString:SetTextColor(fontString.defaultFontObject:GetTextColor())
- fontString:SetSpacing(fontString.defaultFontObject:GetSpacing())
self[regionName] = fontString
self.fontStrings[regionName] = fontString
@@ -141,15 +168,47 @@ ObjectBase.CreateRegion = function(self, regionType, regionName, parentRegion, d
end
if regionType == 'EditBox' then
local editBox = CreateFrame(regionType, nil, parentRegion)
+
+ -- CRITICAL: Set a font IMMEDIATELY with multiple fallbacks
+ if _G.GameFontNormal then
+ pcall(editBox.SetFontObject, editBox, _G.GameFontNormal)
+ end
+ pcall(editBox.SetFont, editBox, "Fonts\\FRIZQT__.TTF", 12, "")
+
+ local testFont = editBox:GetFont()
+ if not testFont and _G.ChatFontNormal then
+ pcall(editBox.SetFontObject, editBox, _G.ChatFontNormal)
+ end
+
-- set Default font properties
if defaultFontObject then
editBox.defaultFontObject = defaultFontObject
else
- editBox.defaultFontObject = DiesalFontNormal
+ editBox.defaultFontObject = _G.DiesalFontNormal or _G.GameFontNormal
+ end
+
+ -- Try to apply custom font if available
+ if editBox.defaultFontObject and type(editBox.defaultFontObject.GetFont) == "function" then
+ local success, fontPath, fontSize, fontFlags = pcall(editBox.defaultFontObject.GetFont, editBox.defaultFontObject)
+ if success and fontPath then
+ pcall(editBox.SetFont, editBox, fontPath, fontSize or 11, fontFlags or "")
+ end
end
- editBox:SetFont(editBox.defaultFontObject:GetFont())
- editBox:SetTextColor(editBox.defaultFontObject:GetTextColor())
- editBox:SetSpacing(editBox.defaultFontObject:GetSpacing())
+
+ -- Set colors and spacing safely
+ if editBox.defaultFontObject and type(editBox.defaultFontObject.GetTextColor) == "function" then
+ local success, r, g, b, a = pcall(editBox.defaultFontObject.GetTextColor, editBox.defaultFontObject)
+ if success and r then
+ pcall(editBox.SetTextColor, editBox, r, g, b, a or 1)
+ end
+ end
+ if editBox.defaultFontObject and type(editBox.defaultFontObject.GetSpacing) == "function" then
+ local success, spacing = pcall(editBox.defaultFontObject.GetSpacing, editBox.defaultFontObject)
+ if success and spacing then
+ pcall(editBox.SetSpacing, editBox, spacing)
+ end
+ end
+
editBox:HookScript('OnEscapePressed', function(this) DiesalGUI:ClearFocus(); end)
editBox:HookScript('OnEditFocusGained', function(this)
DiesalGUI:SetFocus(this); GameTooltip:Hide();
@@ -160,15 +219,46 @@ ObjectBase.CreateRegion = function(self, regionType, regionName, parentRegion, d
end
if regionType == 'ScrollingMessageFrame' then
local srollingMessageFrame = CreateFrame(regionType, nil, parentRegion)
+
+ -- CRITICAL: Set a font IMMEDIATELY with multiple fallbacks
+ if _G.GameFontNormal then
+ pcall(srollingMessageFrame.SetFontObject, srollingMessageFrame, _G.GameFontNormal)
+ end
+ pcall(srollingMessageFrame.SetFont, srollingMessageFrame, "Fonts\\FRIZQT__.TTF", 12, "")
+
+ local testFont = srollingMessageFrame:GetFont()
+ if not testFont and _G.ChatFontNormal then
+ pcall(srollingMessageFrame.SetFontObject, srollingMessageFrame, _G.ChatFontNormal)
+ end
+
-- set Default font properties
if defaultFontObject then
srollingMessageFrame.defaultFontObject = defaultFontObject
else
- srollingMessageFrame.defaultFontObject = DiesalFontNormal
+ srollingMessageFrame.defaultFontObject = _G.DiesalFontNormal or _G.GameFontNormal
+ end
+
+ -- Try to apply custom font if available
+ if srollingMessageFrame.defaultFontObject and type(srollingMessageFrame.defaultFontObject.GetFont) == "function" then
+ local success, fontPath, fontSize, fontFlags = pcall(srollingMessageFrame.defaultFontObject.GetFont, srollingMessageFrame.defaultFontObject)
+ if success and fontPath then
+ pcall(srollingMessageFrame.SetFont, srollingMessageFrame, fontPath, fontSize or 11, fontFlags or "")
+ end
+ end
+
+ -- Set colors and spacing safely
+ if srollingMessageFrame.defaultFontObject and type(srollingMessageFrame.defaultFontObject.GetTextColor) == "function" then
+ local success, r, g, b, a = pcall(srollingMessageFrame.defaultFontObject.GetTextColor, srollingMessageFrame.defaultFontObject)
+ if success and r then
+ pcall(srollingMessageFrame.SetTextColor, srollingMessageFrame, r, g, b, a or 1)
+ end
+ end
+ if srollingMessageFrame.defaultFontObject and type(srollingMessageFrame.defaultFontObject.GetSpacing) == "function" then
+ local success, spacing = pcall(srollingMessageFrame.defaultFontObject.GetSpacing, srollingMessageFrame.defaultFontObject)
+ if success and spacing then
+ pcall(srollingMessageFrame.SetSpacing, srollingMessageFrame, spacing)
+ end
end
- srollingMessageFrame:SetFont(srollingMessageFrame.defaultFontObject:GetFont())
- srollingMessageFrame:SetTextColor(srollingMessageFrame.defaultFontObject:GetTextColor())
- srollingMessageFrame:SetSpacing(srollingMessageFrame.defaultFontObject:GetSpacing())
self[regionName] = srollingMessageFrame
return srollingMessageFrame
@@ -179,7 +269,13 @@ ObjectBase.CreateRegion = function(self, regionType, regionName, parentRegion, d
end
ObjectBase.ResetFonts = function(self)
for name, fontString in pairs(self.fontStrings) do
- fontString:SetFont(fontString.defaultFontObject:GetFont())
+ -- Safe font initialization with fallback
+ local fontPath, fontSize, fontFlags = fontString.defaultFontObject:GetFont()
+ if not fontPath then
+ -- Fallback to WoW default if GetFont returns nil
+ fontPath, fontSize, fontFlags = "Fonts\\FRIZQT__.TTF", 11, ""
+ end
+ fontString:SetFont(fontPath, fontSize, fontFlags)
fontString:SetTextColor(fontString.defaultFontObject:GetTextColor())
fontString:SetSpacing(fontString.defaultFontObject:GetSpacing())
end
diff --git a/Libs/DiesalGUI-1.0/DiesalGUI-1.0.xml b/Libs/DiesalGUI-1.0/DiesalGUI-1.0.xml
index 23722d185d..878b297db4 100644
--- a/Libs/DiesalGUI-1.0/DiesalGUI-1.0.xml
+++ b/Libs/DiesalGUI-1.0/DiesalGUI-1.0.xml
@@ -2,6 +2,7 @@
..\FrameXML\UI.xsd">
+
diff --git a/Libs/DiesalGUI-1.0/Objects/FontString.lua b/Libs/DiesalGUI-1.0/Objects/FontString.lua
new file mode 100644
index 0000000000..22e7b14e32
--- /dev/null
+++ b/Libs/DiesalGUI-1.0/Objects/FontString.lua
@@ -0,0 +1,30 @@
+local DiesalGUI = LibStub("DiesalGUI-1.0")
+
+local Type = "FontString"
+local Version = 1
+
+local function Constructor()
+ local self = DiesalGUI:CreateObjectBase(Type)
+
+ -- Create the actual FontString region
+ local UIParent = _G.UIParent
+ self.frame = CreateFrame("Frame", nil, UIParent)
+ self:CreateRegion("FontString", "fontString", self.frame)
+
+ -- CRITICAL: Ensure font is set on the fontString widget
+ if self.fontString then
+ local testFont = self.fontString:GetFont()
+ if not testFont then
+ -- Font wasn't set in CreateRegion, set it now
+ self.fontString:SetFontObject(_G.GameFontNormal)
+ end
+ end
+
+ function self:SetParent(parent)
+ self.frame:SetParent(parent)
+ end
+
+ return self
+end
+
+DiesalGUI:RegisterObjectConstructor(Type, Constructor, Version)
diff --git a/Libs/DiesalStyle-1.0/DiesalStyle-1.0.lua b/Libs/DiesalStyle-1.0/DiesalStyle-1.0.lua
index 666f4c287b..0fd7e09c6f 100644
--- a/Libs/DiesalStyle-1.0/DiesalStyle-1.0.lua
+++ b/Libs/DiesalStyle-1.0/DiesalStyle-1.0.lua
@@ -21,6 +21,7 @@ DiesalStyle.ReleasedTextures = DiesalStyle.ReleasedTextures or {}
DiesalStyle.TextureFrame = DiesalStyle.TextureFrame or CreateFrame("Frame"); DiesalStyle.TextureFrame:Hide();
DiesalStyle.Colors = DiesalStyle.Colors or {}
DiesalStyle.Formatters = DiesalStyle.Formatters or {}
+DiesalStyle.MediaPath = DiesalStyle.MediaPath or nil -- Store the media path for runtime updates
-- ~~| DiesalStyle UpValues |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
local AddonName = ...
local ReleasedTextures = DiesalStyle.ReleasedTextures
@@ -30,30 +31,75 @@ local Colors = DiesalStyle.Colors
local Formatters = DiesalStyle.Formatters
-- ~~| Locals |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
local OUTLINES = { '_LEFT', '_RIGHT', '_TOP', '_BOTTOM' }
-local MEDIA_PATH = AddonName == 'BadRotations' and string.format("Interface\\AddOns\\Media\\")
- or type(AddonName) ~= 'string' and string.format("Interface\\AddOns\\Media\\")
- or AddonName == 'DiesalLibs' and string.format("Interface\\AddOns\\%s\\%s\\Media\\", AddonName, MAJOR)
- or string.format("Interface\\AddOns\\%s\\Libs\\%s\\Media\\", AddonName, MAJOR)
+local MEDIA_PATH = DiesalStyle.MediaPath or (AddonName == 'BadRotations' and string.format("Interface\\AddOns\\Media\\"))
+or (type(AddonName) ~= 'string' and string.format("Interface\\AddOns\\Media\\"))
+or (AddonName == 'DiesalLibs' and string.format("Interface\\AddOns\\%s\\%s\\Media\\", AddonName, MAJOR))
+or string.format("Interface\\AddOns\\%s\\Libs\\%s\\Media\\", AddonName, MAJOR)
+DiesalStyle.MediaPath = DiesalStyle.MediaPath or MEDIA_PATH -- Initialize if not already set
local DEFAULT_COLOR = 'FFFFFF'
local DEFAULT_GRADIENT_ORIENTATION = 'horizontal'
local DEFAULT_LAYER = 'ARTWORK'
-- ~~| Local Methods |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+-- Helper function to convert absolute path to WoW-compatible relative path
+local function toWoWPath(absolutePath)
+ if not absolutePath then return nil end
+
+ -- If already a relative path, return as-is
+ if not absolutePath:match("^%a:") then
+ return absolutePath
+ end
+
+ -- For unlocker environments, try to convert to Interface path
+ -- Extract everything after "AddOns\"
+ local addonsPath = absolutePath:match("AddOns\\(.+)$")
+ if addonsPath then
+ return "Interface\\AddOns\\" .. addonsPath
+ end
+
+ -- If path can't be converted, return the absolute path anyway
+ -- Some unlockers may allow absolute paths
+ return absolutePath
+end
+
local function addMedia(mediaType, name, mediaFile)
Media[mediaType] = Media[mediaType] or {}
- -- update or create new media entry
- Media[mediaType][name] = MEDIA_PATH .. mediaFile
+
+ -- Use default path if MediaPath is not set (initial load)
+ local basePath = DiesalStyle.MediaPath or "Interface\\AddOns\\Media\\"
+ local fullPath = basePath .. mediaFile
+
+ -- Store both the absolute and converted paths for maximum compatibility
+ Media[mediaType][name] = {
+ absolute = fullPath,
+ relative = toWoWPath(fullPath)
+ }
end
local function getMedia(mediaType, name)
if not Media[mediaType] then
- error('media type: ' .. mediaType .. ' does not exist', 2)
- return
+ return nil
end
if not Media[mediaType][name] then
- error('media: "' .. name .. '" does not exist', 2)
- return
+ return nil
end
- return Media[mediaType][name]
+
+ local mediaEntry = Media[mediaType][name]
+
+ -- If it's the new table format with absolute/relative paths
+ if type(mediaEntry) == "table" then
+ -- Return the absolute path for unlockers to try first
+ return mediaEntry.absolute, mediaEntry.relative
+ end
+
+ -- Old format (string path)
+ return mediaEntry
end
+
+-- Helper function for LibSharedMedia - returns only the first path
+local function getMediaPath(mediaType, name)
+ local absolute, relative = getMedia(mediaType, name)
+ return absolute or relative
+end
+
local function newTexture()
local newTexture = next(ReleasedTextures)
if not newTexture then
@@ -108,7 +154,8 @@ end
local function formatFile(file)
if type(file) ~= 'string' and type(file) ~= 'number' then return end
- return Media.texture[file] or file
+ -- Use getMediaPath to extract the actual path string from the media table
+ return getMediaPath('texture', file) or file
end
local function formatPosition(position)
if type(position) ~= 'table' and type(position) ~= 'number' then return end
@@ -506,28 +553,49 @@ do -- | Set Media |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
addMedia('border', 'shadowNoDist', 'shadowNoDist.tga')
end
do -- | Add LibSharedMedia |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- LibSharedMedia:Register("font", "Calibri Bold", getMedia('font', 'calibrib'))
- LibSharedMedia:Register("font", "Fira Sans", getMedia('font', 'FiraSans'))
+ LibSharedMedia:Register("font", "Calibri Bold", getMediaPath('font', 'calibrib'))
+ LibSharedMedia:Register("font", "Fira Sans", getMediaPath('font', 'FiraSans'))
-- monospaced
- -- LibSharedMedia:Register("font","DejaVuSansMono",getMedia('font','DejaVuSansMono'))
- -- LibSharedMedia:Register("font","Hack",getMedia('font','Hack'))
- -- LibSharedMedia:Register("font","Inconsolata",getMedia('font','Inconsolata'))
- -- LibSharedMedia:Register("font","Fira Sans",getMedia('font','FiraSans'))
- -- LibSharedMedia:Register("font","Source Code Pro",getMedia('font','SourceCodePro'))
+ -- LibSharedMedia:Register("font","DejaVuSansMono",getMediaPath('font','DejaVuSansMono'))
+ -- LibSharedMedia:Register("font","Hack",getMediaPath('font','Hack'))
+ -- LibSharedMedia:Register("font","Inconsolata",getMediaPath('font','Inconsolata'))
+ -- LibSharedMedia:Register("font","Fira Sans",getMediaPath('font','FiraSans'))
+ -- LibSharedMedia:Register("font","Source Code Pro",getMediaPath('font','SourceCodePro'))
-- pixel fonts
- LibSharedMedia:Register("font", "Standard0755", getMedia('font', 'Standard0755'))
- LibSharedMedia:Register("font", "FFF Intelligent", getMedia('font', 'FFF Intelligent Thin Condensed'))
+ LibSharedMedia:Register("font", "Standard0755", getMediaPath('font', 'Standard0755'))
+ LibSharedMedia:Register("font", "FFF Intelligent", getMediaPath('font', 'FFF Intelligent Thin Condensed'))
+ -- textures
+ LibSharedMedia:Register("texture", "DiesalGUIcons", getMediaPath('texture', 'DiesalGUIcons'))
+ LibSharedMedia:Register("texture", "DiesalGUIcons64", getMediaPath('texture', 'DiesalGUIcons64'))
+ LibSharedMedia:Register("texture", "DiesalGUIcons32", getMediaPath('texture', 'DiesalGUIcons32'))
+ LibSharedMedia:Register("texture", "DiesalButtonIcons32", getMediaPath('texture', 'DiesalButtonIcons32'))
end
do -- | Set Fonts |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ -- Create font objects
CreateFont("DiesalFontNormal")
- DiesalFontNormal:SetFont(getMedia('font', 'calibrib'), 11, '')
CreateFont("DiesalFontPixel")
- DiesalFontPixel:SetFont(getMedia('font', 'Standard0755'), 8, '')
CreateFont("DiesalFontPixelOutLine")
- DiesalFontPixelOutLine:SetFont(getMedia('font', 'Standard0755'), 8, "OUTLINE, MONOCHROME")
- DiesalFontPixelOutLine:SetSpacing(2)
CreateFont("DiesalFontPixel2")
- DiesalFontPixel2:SetFont(getMedia('font', 'FFF Intelligent Thin Condensed'), 8, "OUTLINE, MONOCHROME")
+
+ -- Safe font initialization with fallback to default WoW font
+ local function safeSetFont(fontObject, path, size, flags)
+ local status, err = pcall(fontObject.SetFont, fontObject, path, size, flags or '')
+ if not status then
+ -- If custom font fails, try WoW default
+ pcall(fontObject.SetFont, fontObject, "Fonts\\FRIZQT__.TTF", size, flags or '')
+ end
+ end
+
+ local calibribPath = getMedia('font', 'calibrib')
+ safeSetFont(DiesalFontNormal, calibribPath or "Fonts\\FRIZQT__.TTF", 11, '')
+
+ local standard0755Path = getMedia('font', 'Standard0755')
+ safeSetFont(DiesalFontPixel, standard0755Path or "Fonts\\FRIZQT__.TTF", 8, '')
+ safeSetFont(DiesalFontPixelOutLine, standard0755Path or "Fonts\\FRIZQT__.TTF", 8, "OUTLINE, MONOCHROME")
+ if DiesalFontPixelOutLine then DiesalFontPixelOutLine:SetSpacing(2) end
+
+ local fffIntelligentPath = getMedia('font', 'FFF Intelligent Thin Condensed')
+ safeSetFont(DiesalFontPixel2, fffIntelligentPath or "Fonts\\FRIZQT__.TTF", 8, "OUTLINE, MONOCHROME")
end
-- ~~| DiesalStyle API |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--[[ Texture style table format
@@ -1040,6 +1108,138 @@ function DiesalStyle:AddMedia(mediaType, name, mediaFile)
addMedia(mediaType, name, mediaFile)
end
+function DiesalStyle:SetMediaPath(newPath)
+ -- Ensure the path ends with a backslash
+ if not newPath:match("\\$") then
+ newPath = newPath .. "\\"
+ end
+ DiesalStyle.MediaPath = newPath
+end
+
+function DiesalStyle:GetMediaPath()
+ return DiesalStyle.MediaPath
+end
+
+function DiesalStyle:ReloadMedia()
+ -- Clear current media
+ Media.font = {}
+ Media.texture = {}
+ Media.border = {}
+
+ -- Re-add all media with the new path
+ -- Fonts
+ addMedia('font', 'calibrib', 'calibrib.ttf')
+ -- monospaced
+ addMedia('font', 'DejaVuSansMono', 'DejaVuSansMono.ttf')
+ addMedia('font', 'DejaVuSansMonoBold', 'DejaVuSansMono-Bold.ttf')
+
+ addMedia('font', 'FiraMonoMedium', 'FiraMono-Medium.ttf')
+ addMedia('font', 'FiraMonoRegular', 'FiraMono-Regular.ttf')
+ addMedia('font', 'FiraMonoBold', 'FiraMono-Bold.ttf')
+
+ addMedia('font', 'HackRegular', 'Hack-Regular.ttf')
+ addMedia('font', 'HackBold', 'Hack-Bold.ttf')
+
+ addMedia('font', 'InconsolataRegular', 'Inconsolata-Regular.ttf')
+ addMedia('font', 'InconsolataBold', 'Inconsolata-Bold.ttf')
+
+ addMedia('font', 'LucidiaMono', 'LUCON.ttf')
+
+ addMedia('font', 'MonoFur', 'monof55.ttf')
+ addMedia('font', 'MonoFurItalic', 'monof56.ttf')
+
+ addMedia('font', 'OfficeCodeProRegular', 'OfficeCodePro-Regular.ttf')
+ addMedia('font', 'OfficeCodeProMedium', 'OfficeCodePro-Medium.ttf')
+ addMedia('font', 'OfficeCodeProBold', 'OfficeCodePro-Bold.ttf')
+
+ addMedia('font', 'RobotoMonoRegular', 'RobotoMono-Regular.ttf')
+ addMedia('font', 'RobotoMonoMedium', 'RobotoMono-Medium.ttf')
+ addMedia('font', 'RobotoMonoBold', 'RobotoMono-Bold.ttf')
+
+ addMedia('font', 'SourceCodeProRegular', 'SourceCodePro-Regular.ttf')
+ addMedia('font', 'SourceCodeProMedium', 'SourceCodePro-Medium.ttf')
+ addMedia('font', 'SourceCodeProSemibold', 'SourceCodePro-Semibold.ttf')
+ addMedia('font', 'SourceCodeProBold', 'SourceCodePro-Bold.ttf')
+ addMedia('font', 'SourceCodeProBlack', 'SourceCodePro-Black.ttf')
+
+ addMedia('font', 'UbuntuMonoBold', 'UbuntuMono-B.ttf')
+ addMedia('font', 'UbuntuMonoRegular', 'UbuntuMono-R.ttf')
+
+ -- pixel fonts
+ addMedia('font', 'FiraSans', 'FiraSans-Regular.ttf')
+ addMedia('font', 'Standard0755', 'Standard0755.ttf')
+ addMedia('font', 'FFF Intelligent Thin Condensed', 'FFF Intelligent Thin Condensed.ttf')
+ addMedia('texture', 'DiesalGUIcons', 'DiesalGUIcons16x256x128.tga')
+ addMedia('texture', 'DiesalGUIcons64', 'DiesalGUIcons64x256x256.tga')
+ addMedia('texture', 'DiesalGUIcons32', 'DiesalGUIcons32x256x256.tga')
+ addMedia('texture', 'DiesalButtonIcons32', 'DiesalButtonIcons32x128x512.tga')
+ addMedia('border', 'shadow', 'shadow.tga')
+ addMedia('border', 'shadowNoDist', 'shadowNoDist.tga')
+
+ -- Re-register with LibSharedMedia after path change
+ LibSharedMedia:Register("font", "Calibri Bold", getMediaPath('font', 'calibrib'))
+ LibSharedMedia:Register("font", "Fira Sans", getMediaPath('font', 'FiraSans'))
+ LibSharedMedia:Register("font", "Standard0755", getMediaPath('font', 'Standard0755'))
+ LibSharedMedia:Register("font", "FFF Intelligent", getMediaPath('font', 'FFF Intelligent Thin Condensed'))
+ LibSharedMedia:Register("texture", "DiesalGUIcons", getMediaPath('texture', 'DiesalGUIcons'))
+ LibSharedMedia:Register("texture", "DiesalGUIcons64", getMediaPath('texture', 'DiesalGUIcons64'))
+ LibSharedMedia:Register("texture", "DiesalGUIcons32", getMediaPath('texture', 'DiesalGUIcons32'))
+ LibSharedMedia:Register("texture", "DiesalButtonIcons32", getMediaPath('texture', 'DiesalButtonIcons32'))
+
+ -- Reinitialize fonts after media is reloaded
+ self:ReinitializeFonts()
+end
+
+function DiesalStyle:ReinitializeFonts()
+ -- Safe font setter with fallback - tries multiple paths
+ local function safeSetFont(fontObject, absolutePath, relativePath, size, flags)
+ if not fontObject then
+ return
+ end
+
+ local fontSet = false
+
+ -- Try 1: Absolute path (for unlockers that allow it)
+ if absolutePath and not fontSet then
+ local status = pcall(fontObject.SetFont, fontObject, absolutePath, size, flags or '')
+ if status then
+ local verifyPath = fontObject:GetFont()
+ if verifyPath then
+ fontSet = true
+ end
+ end
+ end
+
+ -- Try 2: Relative path (standard WoW format)
+ if relativePath and not fontSet then
+ local status = pcall(fontObject.SetFont, fontObject, relativePath, size, flags or '')
+ if status then
+ local verifyPath = fontObject:GetFont()
+ if verifyPath then
+ fontSet = true
+ end
+ end
+ end
+
+ -- Try 3: WoW default fallback
+ if not fontSet then
+ pcall(fontObject.SetFont, fontObject, "Fonts\\FRIZQT__.TTF", size, flags or '')
+ end
+ end
+
+ -- Reinitialize all global fonts with the new media paths
+ local calibribAbs, calibribRel = getMedia('font', 'calibrib')
+ safeSetFont(DiesalFontNormal, calibribAbs, calibribRel, 11, '')
+
+ local standard0755Abs, standard0755Rel = getMedia('font', 'Standard0755')
+ safeSetFont(DiesalFontPixel, standard0755Abs, standard0755Rel, 8, '')
+ safeSetFont(DiesalFontPixelOutLine, standard0755Abs, standard0755Rel, 8, "OUTLINE, MONOCHROME")
+ if DiesalFontPixelOutLine then DiesalFontPixelOutLine:SetSpacing(2) end
+
+ local fffIntelligentAbs, fffIntelligentRel = getMedia('font', 'FFF Intelligent Thin Condensed')
+ safeSetFont(DiesalFontPixel2, fffIntelligentAbs, fffIntelligentRel, 8, "OUTLINE, MONOCHROME")
+end
+
-- deprecated
--- function DiesalStyle:StyleTexture(txture,style)
diff --git a/Libs/LibGroupInSpecT-1.1/LibGroupInSpecT-1.1.lua b/Libs/LibGroupInSpecT-1.1/LibGroupInSpecT-1.1.lua
deleted file mode 100644
index 439255ae05..0000000000
--- a/Libs/LibGroupInSpecT-1.1/LibGroupInSpecT-1.1.lua
+++ /dev/null
@@ -1,1037 +0,0 @@
--- vim: ts=2 sw=2 ai et fenc=utf8
-
---[[
--- These events can be registered for using the regular CallbackHandler ways.
---
--- "GroupInSpecT_Update", guid, unit, info
--- "GroupInSpecT_Remove, guid
--- "GroupInSpecT_InspectReady", guid, unit
---
--- Where is a table containing some or all of the following:
--- .guid
--- .name
--- .realm
--- .race
--- .race_localized
--- .class
--- .class_localized
--- .class_id
--- .gender -- 2 = male, 3 = female
--- .global_spec_id
--- .spec_index
--- .spec_name_localized
--- .spec_description
--- .spec_icon
--- .spec_background
--- .spec_role
--- .spec_role_detailed
--- .spec_group -- active spec group (1/2/nil)
--- .talents = {
--- [] = {
--- .tier
--- .column
--- .name_localized
--- .icon
--- .talent_id
--- .spell_id
--- }
--- ...
--- }
--- .pvp_talents = {
--- [] = {
--- .name_localized
--- .icon
--- .talent_id
--- .spell_id
--- }
--- ...
--- }
--- .lku -- last known unit id
--- .not_visible
---
--- Functions for external use:
---
--- lib:Rescan (guid or nil)
--- Force a rescan of the given group member GUID, or of all current group members if nil.
---
--- lib:QueuedInspections ()
--- Returns an array of GUIDs of outstanding inspects.
---
--- lib:StaleInspections ()
--- Returns an array of GUIDs for which the data has become stale and is
--- awaiting an update (no action required, the refresh happens internally).
--- Due to Blizzard exposing no events on (re/un)talent, there will be
--- frequent marking of inspect data as being stale.
---
--- lib:GetCachedInfo (guid)
--- Returns the cached info for the given GUID, if available, nil otherwise.
--- Information is cached for current group members only.
---
--- lib:GroupUnits ()
--- Returns an array with the set of unit ids for the current group.
---]]
-local MAJOR, MINOR = "LibGroupInSpecT-1.1", 91
-
-if not LibStub then
- error(MAJOR .. " requires LibStub")
-end
-local lib = LibStub:NewLibrary(MAJOR, MINOR)
-if not lib then
- return
-end
-
-lib.events = lib.events or LibStub("CallbackHandler-1.0"):New(lib)
-if not lib.events then
- error(MAJOR .. " requires CallbackHandler")
-end
-
-local UPDATE_EVENT = "GroupInSpecT_Update"
-local REMOVE_EVENT = "GroupInSpecT_Remove"
-local INSPECT_READY_EVENT = "GroupInSpecT_InspectReady"
-local QUEUE_EVENT = "GroupInSpecT_QueueChanged"
-
-local COMMS_PREFIX = "LGIST11"
-local COMMS_FMT = "1"
-local COMMS_DELIM = "\a"
-
-local INSPECT_DELAY = 1.5
-local INSPECT_TIMEOUT = 10 -- If we get no notification within 10s, give up on unit
-
-local MAX_ATTEMPTS = 2
-
---[===[@debug@
-lib.debug = false
-local function debug (...)
- if lib.debug then -- allow programmatic override of debug output by client addons
- print (...)
- end
-end
---@end-debug@]===]
-
-function lib.events:OnUsed(target, eventname)
- if eventname == INSPECT_READY_EVENT then
- target.inspect_ready_used = true
- end
-end
-
-function lib.events:OnUnused(target, eventname)
- if eventname == INSPECT_READY_EVENT then
- target.inspect_ready_used = nil
- end
-end
-
--- Frame for events
-local frame = _G[MAJOR .. "_Frame"] or CreateFrame("Frame", MAJOR .. "_Frame")
-lib.frame = frame
-frame:Hide()
-frame:UnregisterAllEvents()
-frame:RegisterEvent("PLAYER_LOGIN")
-frame:RegisterEvent("PLAYER_LOGOUT")
-if not frame.OnEvent then
- frame.OnEvent = function(this, event, ...)
- local eventhandler = lib[event]
- return eventhandler and eventhandler(lib, ...)
- end
- frame:SetScript("OnEvent", frame.OnEvent)
-end
-
--- Hide our run-state in an easy-to-dump object
-lib.state = {
- mainq = {},
- staleq = {}, -- inspect queues
- t = 0,
- last_inspect = 0,
- current_guid = nil,
- throttle = 0,
- tt = 0,
- debounce_send_update = 0
-}
-lib.cache = {}
-lib.static_cache = {}
-
--- Note: if we cache NotifyInspect, we have to hook before we cache it!
-if not lib.hooked then
- hooksecurefunc(
- "NotifyInspect",
- function(...)
- return lib:NotifyInspect(...)
- end
- )
- lib.hooked = true
-end
-function lib:NotifyInspect(unit)
- self.state.last_inspect = GetTime()
-end
-
--- Get local handles on the key API functions
-local CanInspect = _G.CanInspect
-local ClearInspectPlayer = _G.ClearInspectPlayer
-local GetClassInfo = _G.GetClassInfo
-local GetNumSubgroupMembers = _G.GetNumSubgroupMembers
-local GetNumSpecializationsForClassID = _G.GetNumSpecializationsForClassID
-local GetPlayerInfoByGUID = _G.GetPlayerInfoByGUID
-local GetInspectSelectedPvpTalent = _G.C_SpecializationInfo.GetInspectSelectedPvpTalent
-local GetInspectSpecialization = _G.GetInspectSpecialization
-local GetSpecialization = _G.GetSpecialization
-local GetSpecializationInfo = _G.GetSpecializationInfo
-local GetSpecializationInfoForClassID = _G.GetSpecializationInfoForClassID
-local GetSpecializationRoleByID = _G.GetSpecializationRoleByID
-local GetSpellInfo = _G.GetSpellInfo
-local GetPvpTalentInfoByID = _G.GetPvpTalentInfoByID
-local GetPvpTalentSlotInfo = _G.C_SpecializationInfo.GetPvpTalentSlotInfo
-local GetTalentInfo = _G.GetTalentInfo
-local GetTalentInfoByID = _G.GetTalentInfoByID
-local IsInRaid = _G.IsInRaid
---local NotifyInspect = _G.NotifyInspect -- Don't cache, as to avoid missing future hooks
-local GetNumClasses = _G.GetNumClasses
-local UnitExists = _G.UnitExists
-local UnitGUID = _G.UnitGUID
-local UnitInParty = _G.UnitInParty
-local UnitInRaid = _G.UnitInRaid
-local UnitIsConnected = _G.UnitIsConnected
-local UnitIsPlayer = _G.UnitIsPlayer
-local UnitIsUnit = _G.UnitIsUnit
-local UnitName = _G.UnitName
-local SendAddonMessage = _G.C_ChatInfo.SendAddonMessage
-local RegisterAddonMessagePrefix = _G.C_ChatInfo.RegisterAddonMessagePrefix
-
-local MAX_TALENT_TIERS = _G.MAX_TALENT_TIERS
-local NUM_TALENT_COLUMNS = _G.NUM_TALENT_COLUMNS
-local NUM_PVP_TALENT_SLOTS = 4
-
-local global_spec_id_roles_detailed = {
- -- Death Knight
- [250] = "tank", -- Blood
- [251] = "melee", -- Frost
- [252] = "melee", -- Unholy
- -- Demon Hunter
- [577] = "melee", -- Havoc
- [581] = "tank", -- Vengeance
- -- Druid
- [102] = "ranged", -- Balance
- [103] = "melee", -- Feral
- [104] = "tank", -- Guardian
- [105] = "healer", -- Restoration
- -- Hunter
- [253] = "ranged", -- Beast Mastery
- [254] = "ranged", -- Marksmanship
- [255] = "melee", -- Survival
- -- Mage
- [62] = "ranged", -- Arcane
- [63] = "ranged", -- Fire
- [64] = "ranged", -- Frost
- -- Monk
- [268] = "tank", -- Brewmaster
- [269] = "melee", -- Windwalker
- [270] = "healer", -- Mistweaver
- -- Paladin
- [65] = "healer", -- Holy
- [66] = "tank", -- Protection
- [70] = "melee", -- Retribution
- -- Priest
- [256] = "healer", -- Discipline
- [257] = "healer", -- Holy
- [258] = "ranged", -- Shadow
- -- Rogue
- [259] = "melee", -- Assassination
- [260] = "melee", -- Combat
- [261] = "melee", -- Subtlety
- -- Shaman
- [262] = "ranged", -- Elemental
- [263] = "melee", -- Enhancement
- [264] = "healer", -- Restoration
- -- Warlock
- [265] = "ranged", -- Affliction
- [266] = "ranged", -- Demonology
- [267] = "ranged", -- Destruction
- -- Warrior
- [71] = "melee", -- Arms
- [72] = "melee", -- Fury
- [73] = "tank" -- Protection
-}
-
-local class_fixed_roles = {
- HUNTER = "DAMAGER",
- MAGE = "DAMAGER",
- ROGUE = "DAMAGER",
- WARLOCK = "DAMAGER"
-}
-
-local class_fixed_roles_detailed = {
- MAGE = "ranged",
- ROGUE = "melee",
- WARLOCK = "ranged"
-}
-
--- Inspects only work after being fully logged in, so track that
-function lib:PLAYER_LOGIN()
- self.state.logged_in = true
-
- self:CacheGameData()
-
- frame:RegisterEvent("INSPECT_READY")
- frame:RegisterEvent("GROUP_ROSTER_UPDATE")
- frame:RegisterEvent("PLAYER_ENTERING_WORLD")
- frame:RegisterEvent("UNIT_LEVEL")
- frame:RegisterEvent("PLAYER_TALENT_UPDATE")
- frame:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED")
- frame:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
- frame:RegisterEvent("UNIT_NAME_UPDATE")
- frame:RegisterEvent("UNIT_AURA")
- frame:RegisterEvent("CHAT_MSG_ADDON")
- RegisterAddonMessagePrefix(COMMS_PREFIX)
-
- local guid = UnitGUID("player")
- local info = self:BuildInfo("player")
- self.events:Fire(UPDATE_EVENT, guid, "player", info)
-end
-
-function lib:PLAYER_LOGOUT()
- self.state.logged_in = false
-end
-
--- Simple timer
-do
- lib.state.t = 0
- if not frame.OnUpdate then -- ticket #4 if the OnUpdate code every changes we should stop borrowing the existing handler
- frame.OnUpdate = function(this, elapsed)
- lib.state.t = lib.state.t + elapsed
- lib.state.tt = lib.state.tt + elapsed
- if lib.state.t > INSPECT_DELAY then
- lib:ProcessQueues()
- lib.state.t = 0
- end
- -- Unthrottle, essentially allowing 1 msg every 3 seconds, but with substantial burst capacity
- if lib.state.tt > 3 and lib.state.throttle > 0 then
- lib.state.throttle = lib.state.throttle - 1
- lib.state.tt = 0
- end
- if lib.state.debounce_send_update > 0 then
- local debounce = lib.state.debounce_send_update - elapsed
- lib.state.debounce_send_update = debounce
- if debounce <= 0 then
- lib:SendLatestSpecData()
- end
- end
- end
- frame:SetScript("OnUpdate", frame.OnUpdate) -- this is good regardless of the handler check above because otherwise a new anonymous function is created every time the OnUpdate code runs
- end
-end
-
--- Internal library functions
-
--- Caches to deal with API shortcomings as well as performance
-lib.static_cache.global_specs = {} -- [gspec] -> { .idx, .name_localized, .description, .icon, .background, .role }
-lib.static_cache.class_to_class_id = {} -- [CLASS] -> class_id
-
--- The talents cache can no longer be pre-fetched on login, but is now constructed class-by-class as we inspect people.
--- This probably means we want to only ever access it through the GetCachedTalentInfo() helper function below.
-lib.static_cache.talents = {} -- [talent_id] -> { .spell_id, .talent_id, .name_localized, .icon, .tier, .column }
-lib.static_cache.pvp_talents = {} -- [talent_id] -> { .spell_id, .talent_id, .name_localized, .icon }
-
-function lib:GetCachedTalentInfo(class_id, tier, col, group, is_inspect, unit)
- local talent_id, name, icon, sel, _, spell_id = GetTalentInfo(tier, col, group, is_inspect, unit)
- if not talent_id then
- --[===[@debug@
- debug ("GetCachedTalentInfo("..tostring(class_id)..","..tier..","..col..","..group..","..tostring(is_inspect)..","..tostring(unit)..") returned nil") --@end-debug@]===]
- return {}
- end
- local class_talents = self.static_cache.talents
- if not class_talents[talent_id] then
- class_talents[talent_id] = {
- spell_id = spell_id,
- talent_id = talent_id,
- name_localized = name,
- icon = icon,
- tier = tier,
- column = col
- }
- end
- return class_talents[talent_id], sel
-end
-
-function lib:GetCachedTalentInfoByID(talent_id)
- local class_talents = self.static_cache.talents
- if talent_id and not class_talents[talent_id] then
- local _, name, icon, _, _, spell_id, _, row, col = GetTalentInfoByID(talent_id)
- if not name then
- --[===[@debug@
- debug ("GetCachedTalentInfoByID("..tostring(talent_id)..") returned nil") --@end-debug@]===]
- return nil
- end
- class_talents[talent_id] = {
- spell_id = spell_id,
- talent_id = talent_id,
- name_localized = name,
- icon = icon,
- tier = row,
- column = col
- }
- end
- return class_talents[talent_id]
-end
-
-function lib:GetCachedPvpTalentInfoByID(talent_id)
- local pvp_talents = self.static_cache.pvp_talents
- if talent_id and not pvp_talents[talent_id] then
- local _, name, icon, _, _, spell_id = GetPvpTalentInfoByID(talent_id)
- if not name then
- --[===[@debug@
- debug ("GetCachedPvpTalentInfo("..tostring(talent_id)..") returned nil") --@end-debug@]===]
- return nil
- end
- pvp_talents[talent_id] = {
- spell_id = spell_id,
- talent_id = talent_id,
- name_localized = name,
- icon = icon
- }
- end
- return pvp_talents[talent_id]
-end
-
-function lib:CacheGameData()
- local gspecs = self.static_cache.global_specs
- gspecs[0] = {} -- Handle no-specialization case
- for class_id = 1, GetNumClasses() do
- for idx = 1, GetNumSpecializationsForClassID(class_id) do
- local gspec_id, name, description, icon, background = GetSpecializationInfoForClassID(class_id, idx)
- gspecs[gspec_id] = {}
- local gspec = gspecs[gspec_id]
- gspec.idx = idx
- gspec.name_localized = name
- gspec.description = description
- gspec.icon = icon
- gspec.background = background
- gspec.role = GetSpecializationRoleByID(gspec_id)
- end
-
- local _, class = GetClassInfo(class_id)
- self.static_cache.class_to_class_id[class] = class_id
- end
-end
-
-function lib:GuidToUnit(guid)
- local info = self.cache[guid]
- if info and info.lku and UnitGUID(info.lku) == guid then
- return info.lku
- end
-
- for i, unit in ipairs(self:GroupUnits()) do
- if UnitExists(unit) and UnitGUID(unit) == guid then
- if info then
- info.lku = unit
- end
- return unit
- end
- end
-end
-
-function lib:Query(unit)
- if not UnitIsPlayer(unit) then
- return
- end -- NPC
-
- if UnitIsUnit(unit, "player") then
- self.events:Fire(UPDATE_EVENT, UnitGUID("player"), "player", self:BuildInfo("player"))
- return
- end
-
- local mainq, staleq = self.state.mainq, self.state.staleq
-
- local guid = UnitGUID(unit)
- if not mainq[guid] then
- mainq[guid] = 1
- staleq[guid] = nil
- self.frame:Show() -- Start timer if not already running
- self.events:Fire(QUEUE_EVENT)
- end
-end
-
-function lib:Refresh(unit)
- local guid = UnitGUID(unit)
- if not guid then
- return
- end
- --[===[@debug@
- debug ("Refreshing "..unit) --@end-debug@]===]
- if not self.state.mainq[guid] then
- self.state.staleq[guid] = 1
- self.frame:Show()
- self.events:Fire(QUEUE_EVENT)
- end
-end
-
-function lib:ProcessQueues()
- if not self.state.logged_in then
- return
- end
- if InCombatLockdown() then
- return
- end -- Never inspect while in combat
- if UnitIsDead("player") then
- return
- end -- You can't inspect while dead, so don't even try
- if InspectFrame and InspectFrame:IsShown() then
- return
- end -- Don't mess with the UI's inspections
-
- local mainq = self.state.mainq
- local staleq = self.state.staleq
-
- if not next(mainq) and next(staleq) then
- --[===[@debug@
- debug ("Main queue empty, swapping main and stale queues") --@end-debug@]===]
- self.state.mainq, self.state.staleq = self.state.staleq, self.state.mainq
- mainq, staleq = staleq, mainq
- end
-
- if (self.state.last_inspect + INSPECT_TIMEOUT) < GetTime() then
- -- If there was an inspect going, it's timed out, so either retry or move it to stale queue
- local guid = self.state.current_guid
- if guid then
- --[===[@debug@
- debug ("Inspect timed out for "..guid) --@end-debug@]===]
-
- local count = mainq and mainq[guid] or (MAX_ATTEMPTS + 1)
- if not self:GuidToUnit(guid) then
- --[===[@debug@
- debug ("No longer applicable, removing from queues") --@end-debug@]===]
- mainq[guid], staleq[guid] = nil, nil
- elseif count > MAX_ATTEMPTS then
- --[===[@debug@
- debug ("Excessive retries, moving to stale queue") --@end-debug@]===]
- mainq[guid], staleq[guid] = nil, 1
- else
- mainq[guid] = count + 1
- end
- self.state.current_guid = nil
- end
- end
-
- if self.state.current_guid then
- return
- end -- Still waiting on our inspect data
-
- for guid, count in pairs(mainq) do
- local unit = self:GuidToUnit(guid)
- if not unit then
- --[===[@debug@
- debug ("No longer applicable, removing from queues") --@end-debug@]===]
- mainq[guid], staleq[guid] = nil, nil
- elseif not CanInspect(unit) or not UnitIsConnected(unit) then
- --[===[@debug@
- debug ("Cannot inspect "..unit..", aka "..(UnitName(unit) or "nil")..", moving to stale queue") --@end-debug@]===]
- mainq[guid], staleq[guid] = nil, 1
- else
- --[===[@debug@
- debug ("Inspecting "..unit..", aka "..(UnitName(unit) or "nil")) --@end-debug@]===]
- mainq[guid] = count + 1
- self.state.current_guid = guid
- NotifyInspect(unit)
- break
- end
- end
-
- if not next(mainq) and not next(staleq) and self.state.throttle == 0 and self.state.debounce_send_update <= 0 then
- frame:Hide() -- Cancel timer, nothing queued and no unthrottling to be done
- end
- self.events:Fire(QUEUE_EVENT)
-end
-
-function lib:UpdatePlayerInfo(guid, unit, info)
- info.class_localized, info.class, info.race_localized, info.race, info.gender, info.name, info.realm = GetPlayerInfoByGUID(guid)
- local class = info.class
- if info.realm and info.realm == "" then
- info.realm = nil
- end
- info.class_id = class and self.static_cache.class_to_class_id[class]
- if not info.spec_role then
- info.spec_role = class and class_fixed_roles[class]
- end
- if not info.spec_role_detailed then
- info.spec_role_detailed = class and class_fixed_roles_detailed[class]
- end
- info.lku = unit
-end
-
-function lib:BuildInfo(unit)
- local guid = UnitGUID(unit)
- if not guid then
- return
- end
-
- local cache = self.cache
- local info = cache[guid] or {}
- cache[guid] = info
- info.guid = guid
-
- self:UpdatePlayerInfo(guid, unit, info)
- -- On a cold login, GetPlayerInfoByGUID() doesn't seem to be usable, so mark as stale
- local class = info.class
- if not class and not self.state.mainq[guid] then
- self.state.staleq[guid] = 1
- self.frame:Show()
- self.events:Fire(QUEUE_EVENT)
- end
-
- local is_inspect = not UnitIsUnit(unit, "player")
- local spec = GetSpecialization()
- local gspec_id = is_inspect and GetInspectSpecialization(unit) or spec and GetSpecializationInfo(spec)
-
- local gspecs = self.static_cache.global_specs
- if not gspec_id or not gspecs[gspec_id] then -- not a valid spec_id
- info.global_spec_id = nil
- else
- info.global_spec_id = gspec_id
- local spec_info = gspecs[gspec_id]
- info.spec_index = spec_info.idx
- info.spec_name_localized = spec_info.name_localized
- info.spec_description = spec_info.description
- info.spec_icon = spec_info.icon
- info.spec_background = spec_info.background
- info.spec_role = spec_info.role
- info.spec_role_detailed = global_spec_id_roles_detailed[gspec_id]
- end
-
- if not info.spec_role then
- info.spec_role = class and class_fixed_roles[class]
- end
- if not info.spec_role_detailed then
- info.spec_role_detailed = class and class_fixed_roles_detailed[class]
- end
-
- info.talents = info.talents or {}
- info.pvp_talents = info.pvp_talents or {}
-
- -- Only scan talents when we have player data
- if info.spec_index then
- info.spec_group = GetActiveSpecGroup(is_inspect)
- wipe(info.talents)
- for tier = 1, MAX_TALENT_TIERS do
- for col = 1, NUM_TALENT_COLUMNS do
- local talent, sel = self:GetCachedTalentInfo(info.class_id, tier, col, info.spec_group, is_inspect, unit)
- if sel then
- info.talents[talent.talent_id] = talent
- end
- end
- end
-
- wipe(info.pvp_talents)
- if is_inspect then
- for index = 1, NUM_PVP_TALENT_SLOTS do
- local talent_id = GetInspectSelectedPvpTalent(unit, index)
- if talent_id then
- info.pvp_talents[talent_id] = self:GetCachedPvpTalentInfoByID(talent_id)
- end
- end
- else
- -- C_SpecializationInfo.GetAllSelectedPvpTalentIDs will sometimes return a lot of extra talents
- for index = 1, NUM_PVP_TALENT_SLOTS do
- local slot_info = GetPvpTalentSlotInfo(index)
- local talent_id = slot_info and slot_info.selectedTalentID
- if talent_id then
- info.pvp_talents[talent_id] = self:GetCachedPvpTalentInfoByID(talent_id)
- end
- end
- end
- end
-
- info.glyphs = info.glyphs or {} -- kept for addons that still refer to this
-
- if is_inspect and not UnitIsVisible(unit) and UnitIsConnected(unit) then
- info.not_visible = true
- end
-
- return info
-end
-
-function lib:INSPECT_READY(guid)
- local unit = self:GuidToUnit(guid)
- local finalize = false
- if unit then
- if guid == self.state.current_guid then
- self.state.current_guid = nil -- Got what we asked for
- finalize = true
- --[===[@debug@
- debug ("Got inspection data for requested guid "..guid) --@end-debug@]===]
- end
-
- local mainq, staleq = self.state.mainq, self.state.staleq
- mainq[guid], staleq[guid] = nil, nil
-
- local gspec_id = GetInspectSpecialization(unit)
- if not self.static_cache.global_specs[gspec_id] then -- Bah, got garbage, flag as stale and try again
- staleq[guid] = 1
- return
- end
-
- self.events:Fire(UPDATE_EVENT, guid, unit, self:BuildInfo(unit))
- self.events:Fire(INSPECT_READY_EVENT, guid, unit)
- end
- if finalize then
- ClearInspectPlayer()
- end
- self.events:Fire(QUEUE_EVENT)
-end
-
-function lib:PLAYER_ENTERING_WORLD()
- if self.commScope == "INSTANCE_CHAT" then
- -- Handle moving directly from one LFG to another
- self.commScope = nil
- self:UpdateCommScope()
- end
-end
-
--- Group handling parts
-
-local members = {}
-function lib:GROUP_ROSTER_UPDATE()
- local group = self.cache
- local units = self:GroupUnits()
- -- Find new members
- for i, unit in ipairs(self:GroupUnits()) do
- local guid = UnitGUID(unit)
- if guid then
- members[guid] = true
- if not group[guid] then
- self:Query(unit)
- -- Update with what we have so far (guid, unit, name/class/race?)
- self.events:Fire(UPDATE_EVENT, guid, unit, self:BuildInfo(unit))
- end
- end
- end
- -- Find removed members
- for guid in pairs(group) do
- if not members[guid] then
- group[guid] = nil
- self.events:Fire(REMOVE_EVENT, guid, nil)
- end
- end
- wipe(members)
- self:UpdateCommScope()
-end
-
-function lib:DoPlayerUpdate()
- self:Query("player")
- self.state.debounce_send_update = 2.5 -- Hold off 2.5sec before sending update
- self.frame:Show()
-end
-
-function lib:SendLatestSpecData()
- local scope = self.commScope
- if not scope then
- return
- end
-
- local guid = UnitGUID("player")
- local info = self.cache[guid]
- if not info then
- return
- end
-
- -- fmt, guid, global_spec_id, talent1 -> MAX_TALENT_TIERS, pvptalent1 -> NUM_PVP_TALENT_SLOTS
- -- sequentially, allow no gaps for missing talents we decode by index on the receiving end.
- local datastr = COMMS_FMT .. COMMS_DELIM .. guid .. COMMS_DELIM .. (info.global_spec_id or 0)
- local talentCount = 1
- for k in pairs(info.talents) do
- datastr = datastr .. COMMS_DELIM .. k
- talentCount = talentCount + 1
- end
- for i = talentCount, MAX_TALENT_TIERS do
- datastr = datastr .. COMMS_DELIM .. 0
- end
- talentCount = 1
- for k in pairs(info.pvp_talents) do
- datastr = datastr .. COMMS_DELIM .. k
- talentCount = talentCount + 1
- end
- for i = talentCount, NUM_PVP_TALENT_SLOTS do
- datastr = datastr .. COMMS_DELIM .. 0
- end
-
- --[===[@debug@
- debug ("Sending LGIST update to "..scope) --@end-debug@]===]
- SendAddonMessage(COMMS_PREFIX, datastr, scope)
-end
-
-function lib:UpdateCommScope()
- local scope = (IsInGroup(LE_PARTY_CATEGORY_INSTANCE) and "INSTANCE_CHAT") or (IsInRaid() and "RAID") or (IsInGroup(LE_PARTY_CATEGORY_HOME) and "PARTY")
- if self.commScope ~= scope then
- self.commScope = scope
- self:DoPlayerUpdate()
- end
-end
-
--- Indicies for various parts of the split data msg
-local msg_idx = {}
-msg_idx.fmt = 1
-msg_idx.guid = msg_idx.fmt + 1
-msg_idx.global_spec_id = msg_idx.guid + 1
-msg_idx.talents = msg_idx.global_spec_id + 1
-msg_idx.end_talents = msg_idx.talents + MAX_TALENT_TIERS
-msg_idx.pvp_talents = msg_idx.end_talents + 1
-msg_idx.end_pvp_talents = msg_idx.pvp_talents + NUM_PVP_TALENT_SLOTS - 1
-
-function lib:CHAT_MSG_ADDON(prefix, datastr, scope, sender)
- if prefix ~= COMMS_PREFIX or scope ~= self.commScope then
- return
- end
- --[===[@debug@
- debug ("Incoming LGIST update from "..(scope or "nil").."/"..(sender or "nil")..": "..(datastr:gsub(COMMS_DELIM,";") or "nil")) --@end-debug@]===]
-
- local data = {strsplit(COMMS_DELIM, datastr)}
- local fmt = data[msg_idx.fmt]
- if fmt ~= COMMS_FMT then
- return
- end -- Unknown format, ignore
-
- local guid = data[msg_idx.guid]
-
- local senderguid = UnitGUID(sender)
- if senderguid and senderguid ~= guid then
- return
- end
-
- local info = guid and self.cache[guid]
- if not info then
- return
- end -- Never allow random message to create new group member entries!
-
- local unit = self:GuidToUnit(guid)
- if not unit then
- return
- end
- if UnitIsUnit(unit, "player") then
- return
- end -- we're already up-to-date, comment out for solo debugging
-
- self.state.throttle = self.state.throttle + 1
- self.frame:Show() -- Ensure we're unthrottling
- if self.state.throttle > 40 then
- return
- end -- If we ever hit this, someone's being "funny"
-
- info.class_localized, info.class, info.race_localized, info.race, info.gender, info.name, info.realm = GetPlayerInfoByGUID(guid)
- if info.realm and info.realm == "" then
- info.realm = nil
- end
- info.class_id = self.static_cache.class_to_class_id[info.class]
-
- local gspecs = self.static_cache.global_specs
-
- local gspec_id = data[msg_idx.global_spec_id] and tonumber(data[msg_idx.global_spec_id])
- if not gspec_id or not gspecs[gspec_id] then
- return
- end -- Malformed message, avoid throwing errors by using this nil
-
- info.global_spec_id = gspec_id
- info.spec_index = gspecs[gspec_id].idx
- info.spec_name_localized = gspecs[gspec_id].name_localized
- info.spec_description = gspecs[gspec_id].description
- info.spec_icon = gspecs[gspec_id].icon
- info.spec_background = gspecs[gspec_id].background
- info.spec_role = gspecs[gspec_id].role
- info.spec_role_detailed = global_spec_id_roles_detailed[gspec_id]
-
- local need_inspect = nil -- shouldn't be needed, but just in case
- info.talents = wipe(info.talents or {})
- for i = msg_idx.talents, msg_idx.end_talents do
- local talent_id = tonumber(data[i]) or 0
- if talent_id > 0 then
- local talent = self:GetCachedTalentInfoByID(talent_id)
- if talent then
- info.talents[talent_id] = talent
- else
- need_inspect = 1
- end
- end
- end
-
- info.pvp_talents = wipe(info.pvp_talents or {})
- for i = msg_idx.pvp_talents, msg_idx.end_pvp_talents do
- local talent_id = tonumber(data[i]) or 0
- if talent_id > 0 then
- local talent = self:GetCachedPvpTalentInfoByID(talent_id)
- if talent then
- info.pvp_talents[talent_id] = talent
- else
- need_inspect = 1
- end
- end
- end
-
- info.glyphs = info.glyphs or {} -- kept for addons that still refer to this
-
- local mainq, staleq = self.state.mainq, self.state.staleq
- local want_inspect = not need_inspect and self.inspect_ready_used and (mainq[guid] or staleq[guid]) and 1 or nil
- mainq[guid], staleq[guid] = need_inspect, want_inspect
- if need_inspect or want_inspect then
- self.frame:Show()
- end
-
- --[===[@debug@
- debug ("Firing LGIST update event for unit "..unit..", GUID "..guid..", inspect "..tostring(not not need_inspect)) --@end-debug@]===]
- self.events:Fire(UPDATE_EVENT, guid, unit, info)
- self.events:Fire(QUEUE_EVENT)
-end
-
-function lib:UNIT_LEVEL(unit)
- if UnitInRaid(unit) or UnitInParty(unit) then
- self:Refresh(unit)
- end
- if UnitIsUnit(unit, "player") then
- self:DoPlayerUpdate()
- end
-end
-
-function lib:PLAYER_TALENT_UPDATE()
- self:DoPlayerUpdate()
-end
-
-function lib:PLAYER_SPECIALIZATION_CHANGED(unit)
- -- This event seems to fire a lot, and for no particular reason *sigh*
- -- if UnitInRaid (unit) or UnitInParty (unit) then
- -- self:Refresh (unit)
- -- end
- if unit and UnitIsUnit(unit, "player") then
- self:DoPlayerUpdate()
- end
-end
-
-function lib:UNIT_NAME_UPDATE(unit)
- local group = self.cache
- local guid = UnitGUID(unit)
- local info = guid and group[guid]
- if info then
- self:UpdatePlayerInfo(guid, unit, info)
- if info.name ~= UNKNOWN then
- self.events:Fire(UPDATE_EVENT, guid, unit, info)
- end
- end
-end
-
--- Always get a UNIT_AURA when a unit's UnitIsVisible() changes
-function lib:UNIT_AURA(unit)
- local group = self.cache
- local guid = UnitGUID(unit)
- local info = guid and group[guid]
- if info then
- if not UnitIsUnit(unit, "player") then
- if UnitIsVisible(unit) then
- if info.not_visible then
- info.not_visible = nil
- --[===[@debug@
- debug (unit..", aka "..(UnitName(unit) or "nil")..", is now visible") --@end-debug@]===]
- if not self.state.mainq[guid] then
- self.state.staleq[guid] = 1
- self.frame:Show()
- self.events:Fire(QUEUE_EVENT)
- end
- end
- elseif UnitIsConnected(unit) then
- --[===[@debug@
- if not info.not_visible then
- debug (unit..", aka "..(UnitName(unit) or "nil")..", is no longer visible")
- end
- --@end-debug@]===]
- info.not_visible = true
- end
- end
- end
-end
-
-function lib:UNIT_SPELLCAST_SUCCEEDED(unit, _, spell_id)
- if spell_id == 200749 then -- Activating Specialization
- self:Query(unit) -- Definitely changed, so high prio refresh
- end
-end
-
--- External library functions
-
-function lib:QueuedInspections()
- local q = {}
- for guid in pairs(self.state.mainq) do
- table.insert(q, guid)
- end
- return q
-end
-
-function lib:StaleInspections()
- local q = {}
- for guid in pairs(self.state.staleq) do
- table.insert(q, guid)
- end
- return q
-end
-
-function lib:IsInspectQueued(guid)
- return guid and ((self.state.mainq[guid] or self.state.staleq[guid]) and true)
-end
-
-function lib:GetCachedInfo(guid)
- local group = self.cache
- return guid and group[guid]
-end
-
-function lib:Rescan(guid)
- local mainq, staleq = self.state.mainq, self.state.staleq
- if guid then
- local unit = self:GuidToUnit(guid)
- if unit then
- if UnitIsUnit(unit, "player") then
- self.events:Fire(UPDATE_EVENT, guid, "player", self:BuildInfo("player"))
- elseif not mainq[guid] then
- staleq[guid] = 1
- end
- end
- else
- for i, unit in ipairs(self:GroupUnits()) do
- if UnitExists(unit) then
- if UnitIsUnit(unit, "player") then
- self.events:Fire(UPDATE_EVENT, UnitGUID("player"), "player", self:BuildInfo("player"))
- else
- local guid = UnitGUID(unit)
- if guid and not mainq[guid] then
- staleq[guid] = 1
- end
- end
- end
- end
- end
- self.frame:Show() -- Start timer if not already running
-
- -- Evict any stale entries
- self:GROUP_ROSTER_UPDATE()
- self.events:Fire(QUEUE_EVENT)
-end
-
-local unitstrings = {
- raid = {"player"}, -- This seems to be needed under certain circumstances. Odd.
- party = {"player"}, -- Player not part of partyN
- player = {"player"}
-}
-for i = 1, 40 do
- table.insert(unitstrings.raid, "raid" .. i)
-end
-for i = 1, 4 do
- table.insert(unitstrings.party, "party" .. i)
-end
-
--- Returns an array with the set of unit ids for the current group
-function lib:GroupUnits()
- local units
- if IsInRaid() then
- units = unitstrings.raid
- elseif GetNumSubgroupMembers() > 0 then
- units = unitstrings.party
- else
- units = unitstrings.player
- end
- return units
-end
-
--- If demand-loaded, we need to synthesize a login event
-if IsLoggedIn() then
- lib:PLAYER_LOGIN()
-end
diff --git a/Libs/LibGroupInSpecT-1.1/LibGroupInSpecT-1.1.toc b/Libs/LibGroupInSpecT-1.1/LibGroupInSpecT-1.1.toc
deleted file mode 100644
index cfc0476d89..0000000000
--- a/Libs/LibGroupInSpecT-1.1/LibGroupInSpecT-1.1.toc
+++ /dev/null
@@ -1,9 +0,0 @@
-## Interface: 80300
-## Title: Lib: GroupInSpecT-1.1
-## Notes: Keeps track of group members and keeps an up-to-date cache of their specialization and talents.
-## Version: 1.4.0
-## Author: Anyia of HordeYakka (Jubei'Thos)
-## X-Category: Library
-
-embeds.xml
-lib.xml
diff --git a/Libs/LibGroupInSpecT-1.1/lib.xml b/Libs/LibGroupInSpecT-1.1/lib.xml
deleted file mode 100644
index f1138e686a..0000000000
--- a/Libs/LibGroupInSpecT-1.1/lib.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
diff --git a/Rotations/Death Knight/Blood/BloodChefLyfe.lua b/Rotations/Death Knight/Blood/BloodChefLyfe.lua
index 11953a7c82..fb4482068b 100644
--- a/Rotations/Death Knight/Blood/BloodChefLyfe.lua
+++ b/Rotations/Death Knight/Blood/BloodChefLyfe.lua
@@ -135,15 +135,15 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdown(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdown(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
--Cooldown Key Toggle
- br.ui:createDropdown(section, "Cooldown Mode", br.dropOptions.Toggle, 3)
+ br.ui:createDropdown(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 3)
--Defensive Key Toggle
- br.ui:createDropdown(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupts Key Toggle
- br.ui:createDropdown(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
-- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = {{
@@ -215,17 +215,17 @@ local levelRequirements = {
-- Action List - Extra
actionList.Extra = function()
-- Death Grip as Taunt or Both (modes 3 or 4)
- local gripMode = br.data.settings[br.selectedSpec][br.selectedProfile]["Death Grip Usage"] or 3
+ local gripMode = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["Death Grip Usage"] or 3
if cast.able.deathGrip() and var.playerLevel >= levelRequirements.deathGrip and (gripMode == 3 or gripMode == 4) then
for i = 1, #enemies.yards40 do
local thisUnit = enemies.yards40[i]
local shouldGrip = false
-- Range check
- local gripRange = br.data.settings[br.selectedSpec][br.selectedProfile]["Death Grip Range"] or 25
+ local gripRange = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["Death Grip Range"] or 25
if unit.distance(thisUnit) > 8 and unit.distance(thisUnit) < gripRange then
-- Check target priority based on selection
- local targetPriority = br.data.settings[br.selectedSpec][br.selectedProfile]["Death Grip Target"] or 1
+ local targetPriority = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["Death Grip Target"] or 1
if targetPriority == 1 then -- Any target
shouldGrip = true
elseif targetPriority == 2 and unit.isCaster(thisUnit) then -- Caster priority
@@ -293,8 +293,8 @@ actionList.Interrupts = function()
local thisUnit = enemies.yards30[i]
if unit.interruptable(thisUnit, ui.value("Interrupt At")) then
-- Death Grip as interrupt (mode 2 or mode 4) - Available at level 25
- local gripMode = br.data.settings[br.selectedSpec][br.selectedProfile]["Death Grip Usage"] or 3
- local gripRange = br.data.settings[br.selectedSpec][br.selectedProfile]["Death Grip Range"] or 25
+ local gripMode = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["Death Grip Usage"] or 3
+ local gripRange = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["Death Grip Range"] or 25
if (gripMode == 2 or gripMode == 4) and
cast.able.deathGrip(thisUnit) and var.playerLevel >= levelRequirements.deathGrip and
unit.distance(thisUnit) > 8 and unit.distance(thisUnit) < gripRange
@@ -366,11 +366,11 @@ actionList.PreCombat = function()
if unit.valid("target") then -- Abilities below this only used when target is valid
-- Death Grip - Available at level 25
- local gripMode = br.data.settings[br.selectedSpec][br.selectedProfile]["Death Grip Usage"] or 3
+ local gripMode = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["Death Grip Usage"] or 3
if (gripMode == 3 or gripMode == 4) and
cast.able.deathGrip("target") and var.playerLevel >= levelRequirements.deathGrip and
not unit.isDummy("target") and unit.distance("target") > 8 and
- unit.distance("target") < (br.data.settings[br.selectedSpec][br.selectedProfile]["Death Grip Range"] or 25) and
+ unit.distance("target") < (br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["Death Grip Range"] or 25) and
(not unit.moving("target") or (unit.moving("target") and not unit.facing("target", "player")))
then
if cast.deathGrip("target") then ui.debug("Casting Death Grip [Pre-Combat]") return true end
@@ -738,7 +738,7 @@ local function runRotation()
if actionList.HighPriority() then return true end
-- Main Rotation based on selected Hero Tree
- local heroTree = br.data.settings[br.selectedSpec][br.selectedProfile]["HeroTree"] or 1
+ local heroTree = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["HeroTree"] or 1
if heroTree == 1 then
-- Deathbringer APL
if actionList.Deathbringer() then return true end
@@ -750,8 +750,9 @@ local function runRotation()
end -- End runRotation
local id = 250 -- Blood Death Knight spec ID
-if br.rotations[id] == nil then br.rotations[id] = {} end
-tinsert(br.rotations[id],{
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+tinsert(br.loader.rotations[id],{
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Death Knight/Blood/BloodCuteOne.lua b/Rotations/Death Knight/Blood/BloodCuteOne.lua
index 5e9f5b8e40..7b47bb16e6 100644
--- a/Rotations/Death Knight/Blood/BloodCuteOne.lua
+++ b/Rotations/Death Knight/Blood/BloodCuteOne.lua
@@ -86,13 +86,13 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
-- Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupt Key Toggle
- br.ui:createDropdownWithout(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
-- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -113,6 +113,7 @@ local charges
local debuff
local enemies
local module
+local pet
local runes
local runicPower
local talent
@@ -330,7 +331,7 @@ actionList.Combat = function()
if actionList.Racials() then return true end
-- Sacrificial Pact
-- sacrificial_pact,if=!buff.dancing_rune_weapon.up&(pet.ghoul.remains<2|target.time_to_die 8 then
+ if br.functions.spell:canInterrupt(thisUnit, ui.value("Interrupt At")) and unit.distance(thisUnit) > 8 then
if cast.deathGrip(thisUnit) then
ui.debug("Casting Death Grip [Int]")
return true
@@ -184,7 +184,7 @@ actionList.PreCombat = function()
end
-- Start Attack
-- actions=auto_attack
- if not br._G.C_Spell.IsAutoRepeatSpell(br._G.GetSpellInfo(6603)) and unit.exists(units.dyn5) and unit.distance(units.dyn5) < 5 then
+ if not br._G.C_Spell.IsAutoRepeatSpell(br.api.wow.GetSpellInfo(6603)) and unit.exists(units.dyn5) and unit.distance(units.dyn5) < 5 then
br._G.StartAttack(units.dyn5)
end
end
@@ -212,7 +212,7 @@ local function runRotation()
talent = br.player.talent
buff = br.player.buff
-- General Locals
- var.haltProfile = (unit.inCombat() and var.profileStop) or unit.mounted() or br.pause() or ui.mode.rotation == 4
+ var.haltProfile = (unit.inCombat() and var.profileStop) or unit.mounted() or br.functions.misc:pause() or ui.mode.rotation == 4
-- Units
units.get(5) -- Makes a variable called, units.dyn5
units.get(40)
@@ -326,9 +326,10 @@ local function runRotation()
end
end -- Pause
end -- End runRotation
-local id = 251
-if br.rotations[id] == nil then br.rotations[id] = {} end
-br._G.tinsert(br.rotations[id], {
+local id = 0
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Death Knight/Frost/FrostCuteOne.lua b/Rotations/Death Knight/Frost/FrostCuteOne.lua
new file mode 100644
index 0000000000..6a0f3bc7e8
--- /dev/null
+++ b/Rotations/Death Knight/Frost/FrostCuteOne.lua
@@ -0,0 +1,744 @@
+local rotationName = "CuteOne"
+
+---------------
+--- Toggles ---
+---------------
+local function createToggles()
+ -- Rotation Button
+ local RotationModes = {
+ [1] = { mode = "Auto", value = 1, overlay = "Automatic Rotation", tip = "Swaps between Single and Multiple based on number of targets in range.", highlight = 1, icon = br.player.spells.howlingBlast },
+ [2] = { mode = "Mult", value = 2, overlay = "Multiple Target Rotation", tip = "Multiple target rotation used.", highlight = 0, icon = br.player.spells.howlingBlast },
+ [3] = { mode = "Sing", value = 3, overlay = "Single Target Rotation", tip = "Single target rotation used.", highlight = 0, icon = br.player.spells.frostStrike },
+ [4] = { mode = "Off", value = 4, overlay = "DPS Rotation Disabled", tip = "Disarm DPS Rotation", highlight = 0, icon = br.player.spells.deathStrike }
+ };
+ br.ui:createToggle(RotationModes, "Rotation", 1, 0)
+ -- Cooldown Button
+ local CooldownModes = {
+ [1] = { mode = "Auto", value = 1, overlay = "Cooldowns Automated", tip = "Automatic Cooldowns - Boss Detection.", highlight = 1, icon = br.player.spells.empowerRuneWeapon },
+ [2] = { mode = "On", value = 2, overlay = "Cooldowns Enabled", tip = "Cooldowns used regardless of target.", highlight = 1, icon = br.player.spells.empowerRuneWeapon },
+ [3] = { mode = "Off", value = 3, overlay = "Cooldowns Disabled", tip = "No Cooldowns will be used.", highlight = 0, icon = br.player.spells.empowerRuneWeapon }
+ };
+ br.ui:createToggle(CooldownModes, "Cooldown", 2, 0)
+ -- Defensive Button
+ local DefensiveModes = {
+ [1] = { mode = "On", value = 1, overlay = "Defensive Enabled", tip = "Includes Defensive Cooldowns.", highlight = 1, icon = br.player.spells.iceboundFortitude },
+ [2] = { mode = "Off", value = 2, overlay = "Defensive Disabled", tip = "No Defensives will be used.", highlight = 0, icon = br.player.spells.iceboundFortitude }
+ };
+ br.ui:createToggle(DefensiveModes, "Defensive", 3, 0)
+ -- Interrupt Button
+ local InterruptModes = {
+ [1] = { mode = "On", value = 1, overlay = "Interrupt Enabled", tip = "Enabled Interrupt", highlight = 1, icon = br.player.spells.mindFreeze },
+ [2] = { mode = "Off", value = 2, overlay = "Interrupt Disabled", tip = "Disable Interrupt", highlight = 0, icon = br.player.spells.mindFreeze }
+ };
+ br.ui:createToggle(InterruptModes, "Interrupt", 4, 0)
+end
+
+---------------
+--- OPTIONS ---
+---------------
+local function createOptions()
+ local optionTable
+
+ local function rotationOptions()
+ local section
+ -----------------------
+ --- GENERAL OPTIONS ---
+ -----------------------
+ section = br.ui:createSection(br.ui.window.profile, "General")
+ -- Dummy DPS Test
+ br.ui:createSpinner(section, "DPS Testing", 5, 5, 60, 5,
+ "|cffFFFFFFSet to desired time for test in minuts. Min: 5 / Max: 60 / Interval: 5")
+ -- Death Grip
+ br.ui:createCheckbox(section, "Death Grip")
+ -- Horn of Winter
+ br.ui:createCheckbox(section, "Horn of Winter")
+ -- Lichborne: allow breaking CC and enable self-heal via Death Coil
+ br.ui:createCheckbox(section, "Lichborne")
+ br.ui:createSpinnerWithout(section, "Lichborne Heal At", 35, 0, 95, 5,
+ "|cffFFFFFFHealth percent to cast Lichborne+Death Coil on yourself")
+ br.ui:checkSectionState(section)
+ ------------------------
+ --- COOLDOWN OPTIONS ---
+ ------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Cooldowns")
+ local alwaysCdNever = { "Always", "|cff0000ffCD", "|cffff0000Never" }
+ local alwaysCdAoENever = { "Always", "|cff008000AOE", "|cffffff00AOE/CD", "|cff0000ffCD", "|cffff0000Never" }
+ -- Army of the Dead
+ br.ui:createDropdownWithout(section, "Army of the Dead", alwaysCdAoENever, 3, "|cffFFFFFFWhen to use Army of the Dead.")
+ -- Empower Rune Weapon
+ br.ui:createDropdownWithout(section, "Empower Rune Weapon", alwaysCdAoENever, 3, "|cffFFFFFFWhen to use Empower Rune Weapon.")
+ -- Pillar of Frost
+ br.ui:createDropdownWithout(section, "Pillar of Frost", alwaysCdAoENever, 3, "|cffFFFFFFWhen to use Pillar of Frost.")
+ -- Raise Dead
+ br.ui:createDropdownWithout(section, "Raise Dead", alwaysCdAoENever, 3, "|cffFFFFFFWhen to use Raise Dead.")
+ br.ui:checkSectionState(section)
+ -------------------------
+ --- DEFENSIVE OPTIONS ---
+ -------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Defensive")
+ -- Death Strike
+ br.ui:createSpinner(section, "Death Strike", 50, 0, 95, 5,
+ "|cffFFFFFFHealth Percent to Cast At")
+ -- Death Pact
+ br.ui:createSpinner(section, "Death Pact", 30, 0, 95, 5,
+ "|cffFFFFFFHealth Percent to Cast Death Pact At")
+ -- Raise Dead to enable Death Pact
+ br.ui:createSpinner(section, "Death Pact Raise Dead", 30, 0, 95, 5,
+ "|cffFFFFFFHealth Percent to Cast Raise Dead and Death Pact At")
+ -- Icebound Fortitude
+ br.ui:createSpinner(section, "Icebound Fortitude", 40, 0, 95, 5,
+ "|cffFFFFFFHealth Percent to Cast At")
+ br.ui:checkSectionState(section)
+ -------------------------
+ --- INTERRUPT OPTIONS ---
+ -------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Interrupt")
+ -- Death Grip (Interrupt)
+ br.ui:createCheckbox(section, "Death Grip (Interrupt)")
+ -- Mind Freeze
+ br.ui:createCheckbox(section, "Mind Freeze")
+ -- Strangulate
+ br.ui:createCheckbox(section, "Strangulate")
+ -- "Interrupt At"
+ br.ui:createSpinnerWithout(section, "Interrupt At", 0, 0, 95, 5,
+ "|cffFFFFFFCast Percent to Cast At (0 is random)")
+ br.ui:checkSectionState(section)
+ ----------------------
+ --- TOGGLE OPTIONS ---
+ ----------------------
+ section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
+ -- Single/Multi Toggle
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
+ -- Cooldown Key Toggle
+ br.ui:createDropdownWithout(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 3)
+ -- Defensive Key Toggle
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
+ -- Interrupt Key Toggle
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
+ br.ui:checkSectionState(section)
+ end
+ optionTable = { {
+ [1] = "Rotation Options",
+ [2] = rotationOptions,
+ } }
+ return optionTable
+end
+
+--------------
+--- Locals ---
+--------------
+-- BR API Locals
+local buff
+local cast
+local cd
+local debuff
+local enemies
+local module
+local runes
+local runicPower
+local ui
+local unit
+local units
+local spell
+local talent
+local use
+local var
+-- Profile Specific Locals
+local actionList = {}
+
+--------------------
+--- Action Lists ---
+--------------------
+-- Action List - Extra
+actionList.Extra = function()
+ -- * Dummy Test
+ if ui.checked("DPS Testing") then
+ if unit.exists("target") then
+ if unit.combatTime() >= (tonumber(ui.value("DPS Testing")) * 60) and unit.isDummy() then
+ unit.stopAttack()
+ unit.clearTarget()
+ ui.print(tonumber(ui.value("DPS Testing")) .. " Minute Dummy Test Concluded - Profile Stopped")
+ var.profileStop = true
+ end
+ end
+ end -- End Dummy Test
+end -- End Action List - Extra
+-- Action List - Defensive
+actionList.Defensive = function()
+ if ui.useDefensive() then
+ -- * Death Pact: try to use if available, otherwise optionally raise ghoul to enable
+ if ui.checked("Death Pact") then
+ -- If Death Pact is available, use it
+ if cast.able.deathPact() and GetTime() < var.raiseDeadExpiresAt
+ and unit.hp("player") <= ui.value("Death Pact")
+ then
+ if cast.deathPact() then
+ ui.debug("Casting Death Pact [Defensive]")
+ return true
+ end
+ end
+ -- If not available, optionally raise ghoul to enable Death Pact
+ if ui.checked("Death Pact Raise Dead") and cast.able.raiseDead()
+ and unit.hp("player") <= ui.value("Death Pact Raise Dead")
+ then
+ if cast.raiseDead() then
+ ui.debug("Casting Raise Dead - to enable Death Pact [Defensive]")
+ var.raiseDeadExpiresAt = GetTime() + 60
+ return true
+ end
+ end
+ end
+ -- * Death Strike (Dark Succor)
+ if ui.checked("Death Strike") and unit.inCombat() and buff.darkSuccor.exists() then
+ for i = 1, #enemies.yards5f do
+ local thisUnit = enemies.yards5f[i]
+ if buff.darkSuccor.exists()
+ and (buff.darkSuccor.remain() < unit.gcd(true) or unit.ttd(thisUnit) < unit.gcd(true) or unit.hp("player") <= 80)
+ then
+ if buff.darkSuccor.exists() and cast.able.deathStrike(thisUnit) then
+ if cast.deathStrike(thisUnit) then
+ ui.debug("Casting Death Strike - Dark Succor [Defensive]")
+ return true
+ end
+ end
+ end
+ end
+ end
+ -- * Death Strike
+ if ui.checked("Death Strike") and cast.able.deathStrike() then
+ if unit.hp("player") <= tonumber(ui.value("Death Strike")) and runes.frost() > 0 and runes.unholy() > 0 then
+ if cast.deathStrike() then
+ ui.debug("Casting Death Strike [Defensive]")
+ return true
+ end
+ end
+ end
+ -- * Icebound Fortitude
+ if ui.checked("Icebound Fortitude") and cast.able.iceboundFortitude() and not unit.inCombat() then
+ if unit.hp("player") <= tonumber(ui.value("Icebound Fortitude")) then
+ if cast.iceboundFortitude() then
+ ui.debug("Casting Icebound Fortitude [Defensive]")
+ return true
+ end
+ end
+ end
+ -- * Lichborne: break CC or enable self-heal via Death Coil
+ if ui.checked("Lichborne") and talent.lichborne then
+ -- Break CC: Charm / Fear / Sleep
+ if cast.able.lichborne() and cast.noControl.lichborne() then
+ if cast.lichborne() then
+ ui.debug("Casting Lichborne - Break CC [Defensive]")
+ return true
+ end
+ end
+ -- Self-heal: if lichborne buff present, cast Death Coil on player
+ if buff.lichborne.exists() then
+ if cast.able.deathCoil("player") and unit.hp("player") <= tonumber(ui.value("Lichborne Heal At")) then
+ if cast.deathCoil("player") then
+ ui.debug("Casting Death Coil -> Heal Self (via Lichborne) [Defensive]")
+ return true
+ end
+ end
+ else
+ -- If low health and Lichborne is available, cast it to enable the heal next tick
+ if cast.able.lichborne() and unit.hp("player") <= tonumber(ui.value("Lichborne Heal At")) then
+ if cast.lichborne() then
+ ui.debug("Casting Lichborne - Prepare Self-Heal [Defensive]")
+ return true
+ end
+ end
+ end
+ end
+ end
+end -- End Action List - Defensive
+
+-- Action List - Interrupts
+actionList.Interrupts = function()
+ if ui.useInterrupt() then
+ for i = 1, #enemies.yards30 do
+ local thisUnit = enemies.yards30[i]
+ if unit.interruptable(thisUnit, ui.value("Interrupt At")) and unit.distance(thisUnit) > 8 then
+ -- * Strangulate
+ if ui.checked("Strangulate") and cast.able.strangulate(thisUnit) and runes.blood() > 0 then
+ if cast.strangulate(thisUnit) then
+ ui.debug("Casting Strangulate [Int]")
+ return true
+ end
+ end
+ -- * Death Grip
+ if ui.checked("Death Grip (Interrupt)") and cast.able.deathGrip()
+ and (not spell.strangulate.known() or (cd.strangulate.remains() > 0 and cd.strangulate.remains() < 55))
+ then
+ if cast.deathGrip(thisUnit) then
+ ui.debug("Casting Death Grip [Int]")
+ return true
+ end
+ end
+ end
+ end
+ for i = 1, #enemies.yards5 do
+ local thisUnit = enemies.yards5[i]
+ if unit.interruptable(thisUnit, ui.value("Interrupt At")) then
+ -- * Mind Freeze
+ if ui.checked("Mind Freeze") and cast.able.mindFreeze(thisUnit) then
+ if cast.mindFreeze(thisUnit) then
+ ui.debug("Casting Mind Freeze [Int]")
+ return true
+ end
+ end
+ end
+ end
+ end
+end -- End Action List - Interrupts
+
+-- Action List - AOE
+actionList.AOE = function()
+ -- * Unholy Blight
+ -- unholy_blight,if=talent.unholy_blight.enabled
+ if talent.unholyBlight and cast.able.unholyBlight(nil,"aoe",1,8) then
+ if cast.unholyBlight(nil,"aoe",1,8) then ui.debug("Casting Unholy Blight [AOE]") return true end
+ end
+ -- * Pestilence (spread plague if blood_plague ticking and talent)
+ -- pestilence,if=dot.blood_plague.ticking&talent.plague_leech.enabled,line_cd=28
+ if talent.plagueLeech and cast.able.pestilence() and debuff.bloodPlague.exists(units.dyn5) and debuff.frostFever.exists(units.dyn5)
+ and ui.delay("pestilenceLineCD",28)
+ then
+ if cast.pestilence() then ui.debug("Casting Pestilence - Plague Leech [AOE]") return true end
+ end
+ -- pestilence,if=dot.blood_plague.ticking&talent.unholy_blight.enabled&cooldown.unholy_blight.remains<49,line_cd=28
+ if talent.unholyBlight and cast.able.pestilence() and debuff.bloodPlague.exists(units.dyn5) and debuff.frostFever.exists(units.dyn5)
+ and cd.unholyBlight.remains() < 49 and ui.delay("pestilenceLineCD2",28)
+ then
+ if cast.pestilence() then ui.debug("Casting Pestilence - Unholy Blight [AOE]") return true end
+ end
+ -- * Howling Blast
+ -- howling_blast
+ if (ui.mode.rotation == 3 or not cast.safe.howlingBlast(units.dyn5,"targetAOE",1,10)) and cast.able.icyTouch(units.dyn5) then
+ if cast.icyTouch(units.dyn5) then ui.debug("Casting Icy Touch [AOE]") return true end
+ elseif cast.able.howlingBlast(units.dyn5,"targetAOE",1,10) then
+ if cast.howlingBlast(units.dyn5,"targetAOE",1,10) then ui.debug("Casting Howling Blast [AOE]") return true end
+ end
+ -- * Blood Tap
+ -- blood_tap,if=talent.blood_tap.enabled&buff.blood_charge.stack>1
+ if talent.bloodTap and cast.able.bloodTap() and buff.bloodCharge.stack() > 4 and runes() < 5 then
+ if cast.bloodTap() then ui.debug("Casting Blood Tap - Blood Charges [AOE]") return true end
+ end
+ -- * Frost Strike when RP high
+ -- frost_strike,if=runic_power>76
+ if cast.able.frostStrike() and runicPower() > 76 then
+ if cast.frostStrike() then ui.debug("Casting Frost Strike - High RP [AOE]") return true end
+ end
+ -- * Death and Decay
+ -- death_and_decay,if=unholy=1
+ if cast.able.deathAndDecay("best",nil,1,8) and runes.unholy() == 1 then
+ if cast.deathAndDecay("best",nil,1,8) then ui.debug("Casting Death and Decay [AOE]") return true end
+ end
+ -- * Plague Strike
+ -- plague_strike,if=unholy=2
+ if cast.able.plagueStrike() and runes.unholy() == 2 then
+ if cast.plagueStrike() then ui.debug("Casting Plague Strike - 2 Ungoly Runes [AOE]") return true end
+ end
+ -- * Blood Tap
+ -- blood_tap,if=talent.blood_tap.enabled
+ if talent.bloodTap and cast.able.bloodTap() and buff.bloodCharge.stack() > 4 and runes() < 5 then
+ if cast.bloodTap() then ui.debug("Casting Blood Tap [AOE]") return true end
+ end
+ -- * Frost Strike
+ -- frost_strike
+ if cast.able.frostStrike() then
+ if cast.frostStrike() then ui.debug("Casting Frost Strike [AOE]") return true end
+ end
+ -- * Horn of Winter
+ -- horn_of_winter
+ if ui.checked("Horn of Winter") and cast.able.hornOfWinter() then
+ if cast.hornOfWinter() then ui.debug("Casting Horn of Winter [AOE]") return true end
+ end
+ -- * Plague Leech
+ -- plague_leech,if=talent.plague_leech.enabled&unholy=1
+ if talent.plagueLeech and runes() < 5 and runes.unholy() == 1 then
+ for i = 1, #enemies.yards30 do
+ local thisUnit = enemies.yards30[i]
+ if cast.able.plagueLeech(thisUnit) and debuff.bloodPlague.exists(thisUnit) and debuff.frostFever.exists(thisUnit) then
+ if cast.plagueLeech(thisUnit) then ui.debug("Casting Plague Leech [AOE]") return true end
+ end
+ end
+ end
+ -- * Plague Strike
+ -- plague_strike,if=unholy=1
+ if cast.able.plagueStrike() and runes.unholy() == 1 then
+ if cast.plagueStrike() then ui.debug("Casting Plague Strike - 1 Unholy Rune [AOE]") return true end
+ end
+ -- * Empower Rune Weapon
+ -- empower_rune_weapon
+ if ui.alwaysCdAoENever("Empower Rune Weapon",3,#enemies.yards8) and cast.able.empowerRuneWeapon()
+ and runes() == 0 and runicPower() <= 75
+ then
+ if cast.empowerRuneWeapon() then ui.debug("Casting Empower Rune Weapon [AOE]") return true end
+ end
+end -- End Action List - AOE
+
+-- Action List - Single Target
+actionList.SingleTarget = function()
+ -- * Blood Tap: use to convert charges when stacks high and RP conditions met
+ -- blood_tap,if=talent.blood_tap.enabled&(buff.blood_charge.stack>10&(runic_power>76|(runic_power>=20&buff.killing_machine.react)))
+ if talent.bloodTap and cast.able.bloodTap() and buff.bloodCharge.stack() > 10 and runes() < 5
+ and (runicPower() > 76 or (runicPower() >= 20 and buff.killingMachine.exists()))
+ then
+ if cast.bloodTap() then ui.debug("Casting Blood Tap - Killing Machine | High RP [Single]") return true end
+ end
+ -- * Frost Strike on Killing Machine or very high RP
+ -- frost_strike,if=buff.killing_machine.react|runic_power>88
+ if cast.able.frostStrike() and (buff.killingMachine.exists() or runicPower() > 88) then
+ if cast.frostStrike() then ui.debug("Casting Frost Strike - Killing Machine | High RP [Single]") return true end
+ end
+ -- * Howling Blast: apply Frost Fever if missing or use on rime proc
+ -- howling_blast,if=death>1|frost>1
+ if (runes.death() > 1 or runes.frost() > 1) then
+ if (ui.mode.rotation == 3 or not cast.safe.howlingBlast(units.dyn5,"targetAOE",1,10)) and cast.able.icyTouch(units.dyn5) then
+ if cast.icyTouch(units.dyn5) then ui.debug("Casting Icy Touch - Multiple Death or Frost Runes [Single]") return true end
+ elseif cast.able.howlingBlast(units.dyn5,"targetAOE",1,10) then
+ if cast.howlingBlast(units.dyn5,"targetAOE",1,10) then ui.debug("Casting Howling Blast - Multiple Death or Frost Runes [Single]") return true end
+ end
+ end
+ -- * Unholy Blight
+ -- unholy_blight,if=talent.unholy_blight.enabled&((dot.frost_fever.remains<3|dot.blood_plague.remains<3))
+ if talent.unholyBlight and cast.able.unholyBlight() and
+ (debuff.frostFever.remains(units.dyn5) < 3 or debuff.bloodPlague.remains(units.dyn5) < 3)
+ then
+ if cast.unholyBlight() then ui.debug("Casting Unholy Blight [Single]") return true end
+ end
+ -- * Soul Reaper (execute range) - translate simc expression safely
+ -- soul_reaper,if=target.health.pct-3*(target.health.pct%target.time_to_die)<=45
+ if cast.able.soulReaper() and (unit.hp(units.dyn5) - 3 * (unit.hp(units.dyn5) / unit.ttd(units.dyn5))) <= 45 then
+ if cast.soulReaper() then ui.debug("Casting Soul Reaper [Single]") return true end
+ end
+ -- * Blood Tap
+ -- blood_tap,if=talent.blood_tap.enabled&((target.health.pct-3*(target.health.pct%target.time_to_die)<=45&cooldown.soul_reaper.remains=0))
+ if talent.bloodTap and cast.able.bloodTap() and buff.bloodCharge.stack() > 4 and runes() < 5
+ and ((unit.hp(units.dyn5) - 3 * (unit.hp(units.dyn5) / unit.ttd(units.dyn5))) <= 45 and cd.soulReaper.remains() == 0)
+ then
+ if cast.bloodTap() then ui.debug("Casting Blood Tap - Soul Reaper Available [Single]") return true end
+ end
+ -- * Howling Blast
+ -- howling_blast,if=!dot.frost_fever.ticking
+ if not debuff.frostFever.exists(units.dyn5) then
+ if (ui.mode.rotation == 3 or not cast.safe.howlingBlast(units.dyn5,"targetAOE",1,10)) and cast.able.icyTouch(units.dyn5) then
+ if cast.icyTouch(units.dyn5) then ui.debug("Casting Icy Touch - No Frost Fever [Single]") return true end
+ elseif cast.able.howlingBlast(units.dyn5,"targetAOE",1,10) then
+ if cast.howlingBlast(units.dyn5,"targetAOE",1,10) then ui.debug("Casting Howling Blast - No Frost Fever [Single]") return true end
+ end
+ end
+ -- * Plague Strike: apply Blood Plague if missing (unholy presence handled elsewhere)
+ -- plague_strike,if=!dot.blood_plague.ticking&unholy>0
+ if cast.able.plagueStrike() and not debuff.bloodPlague.exists(units.dyn5) and runes.unholy() > 0 then
+ if cast.plagueStrike() then ui.debug("Casting Plague Strike [Single]") return true end
+ end
+ -- * Howling Blast
+ -- howling_blast,if=buff.rime.react
+ if buff.rime.exists() then
+ if (ui.mode.rotation == 3 or not cast.safe.howlingBlast(units.dyn5,"targetAOE",1,10)) and cast.able.icyTouch(units.dyn5) then
+ if cast.icyTouch(units.dyn5) then ui.debug("Casting Icy Touch - Rime Proc [Single]") return true end
+ elseif cast.able.howlingBlast(units.dyn5,"targetAOE",1,10) then
+ if cast.howlingBlast(units.dyn5,"targetAOE",1,10) then ui.debug("Casting Howling Blast - Rime Proc [Single]") return true end
+ end
+ end
+ -- * Frost Strike
+ -- frost_strike,if=runic_power>76
+ if cast.able.frostStrike() and runicPower() > 76 then
+ if cast.frostStrike() then ui.debug("Casting Frost Strike - High RP [Single]") return true end
+ end
+ -- * Obliterate
+ -- obliterate,if=unholy>0&!buff.killing_machine.react
+ if cast.able.obliterate() and runes.unholy() > 0 and not buff.killingMachine.exists() then
+ if cast.obliterate() then ui.debug("Casting Obliterate [Single]") return true end
+ end
+ -- * Howling Blast / Icy Touch
+ -- howling_blast
+ if (ui.mode.rotation == 3 or not cast.safe.howlingBlast(units.dyn5,"targetAOE",1,10)) and cast.able.icyTouch(units.dyn5) then
+ if cast.icyTouch(units.dyn5) then ui.debug("Casting Icy Touch [Single]") return true end
+ elseif cast.able.howlingBlast(units.dyn5,"targetAOE",1,10) then
+ if cast.howlingBlast(units.dyn5,"targetAOE",1,10) then ui.debug("Casting Howling Blast [Single]") return true end
+ end
+ -- * Frost Strike
+ -- frost_strike,if=talent.runic_empowerment.enabled&unholy=1
+ if talent.runicEmpowerment and cast.able.frostStrike() and runes.unholy() == 1 then
+ if cast.frostStrike() then ui.debug("Casting Frost Strike - Runic Empowerment Single Unholy Rune [Single]") return true end
+ end
+ -- * Blood Tap
+ -- blood_tap,if=talent.blood_tap.enabled&(target.health.pct-3*(target.health.pct%target.time_to_die)>35|buff.blood_charge.stack>=8)
+ if talent.bloodTap and cast.able.bloodTap() and buff.bloodCharge.stack() > 4 and runes() < 5
+ and ((unit.hp(units.dyn5) - 3 * (unit.hp(units.dyn5) / unit.ttd(units.dyn5))) > 35 or buff.bloodCharge.stack() >= 8)
+ then
+ if cast.bloodTap() then ui.debug("Casting Blood Tap - Execute | High Blood Charges [Single]") return true end
+ end
+ -- * Frost Strike
+ -- frost_strike,if=runic_power>=40
+ if cast.able.frostStrike() and runicPower() >= 40 then
+ if cast.frostStrike() then ui.debug("Casting Frost Strike [Single]") return true end
+ end
+ -- * Horn of Winter
+ -- horn_of_winter
+ if ui.checked("Horn of Winter") and cast.able.hornOfWinter() then
+ if cast.hornOfWinter() then ui.debug("Casting Horn of Winter [Single]") return true end
+ end
+ -- * Blood Tap
+ -- blood_tap,if=talent.blood_tap.enabled
+ if talent.bloodTap and cast.able.bloodTap() and buff.bloodCharge.stack() > 4 and runes() < 5 then
+ if cast.bloodTap() then ui.debug("Casting Blood Tap [Single]") return true end
+ end
+ -- * Plague Leech: consume diseases to generate Runic Power when diseases are present and RP is not high
+ -- plague_leech,if=talent.plague_leech.enabled
+ if talent.plagueLeech and runes() < 5 then
+ for i = 1, #enemies.yards30 do
+ local thisUnit = enemies.yards30[i]
+ if cast.able.plagueLeech(thisUnit) and debuff.bloodPlague.exists(thisUnit) and debuff.frostFever.exists(thisUnit) then
+ if cast.plagueLeech(thisUnit) then ui.debug("Casting Plague Leech [Single]") return true end
+ end
+ end
+ end
+ -- * Empower Rune Weapon
+ -- empower_rune_weapon
+ if ui.alwaysCdAoENever("Empower Rune Weapon",3,#enemies.yards8) and cast.able.empowerRuneWeapon()
+ and runes() == 0 and runicPower() <= 75
+ then
+ if cast.empowerRuneWeapon() then ui.debug("Casting Empower Rune Weapon [Single]") return true end
+ end
+end -- End Action List - Single Target
+
+-- Action List - Pre-Combat
+actionList.PreCombat = function()
+ if not unit.inCombat() and not (unit.flying() or unit.mounted() or unit.taxi()) then
+ -- * Flask
+ -- flask,type=winters_bite
+ -- * Food
+ -- food,type=black_pepper_ribs_and_shrimp
+ if unit.valid("target") then
+ -- * Horn of Winter
+ -- horn_of_winter
+ if ui.checked("Horn of Winter") and cast.able.hornOfWinter() and unit.distance("target") < 8 then
+ if cast.hornOfWinter() then ui.debug("Casting Horn of Winter [Precombat]") return true end
+ end
+ -- * Frost Presence
+ -- frost_presence
+ if cast.able.frostPresence() and not buff.frostPresence.exists() then
+ if cast.frostPresence() then ui.debug("Casting Frost Presence [Precombat]") return true end
+ end
+ -- * Army of the Dead
+ -- army_of_the_dead
+ if ui.alwaysCdAoENever("Army of the Dead",3,#enemies.yards8) and cast.able.armyOfTheDead() then
+ if cast.armyOfTheDead() then ui.debug("Casting Army of the Dead [Precombat]") return true end
+ end
+ -- * Potion
+ -- mogu_power_potion
+ if module and module.CombatPotionUp then
+ module.CombatPotionUp()
+ end
+ -- * Pillar of Frost
+ -- pillar_of_frost
+ if ui.alwaysCdAoENever("Pillar of Frost",3,#enemies.yards8) and cast.able.pillarOfFrost() then
+ if cast.pillarOfFrost() then
+ ui.debug("Casting Pillar of Frost [Precombat]")
+ return true
+ end
+ end
+ -- * Raise Dead
+ -- raise_dead
+ if ui.alwaysCdAoENever("Raise Dead",3,#enemies.yards8) and cast.able.raiseDead() then
+ if cast.raiseDead() then
+ ui.debug("Casting Raise Dead [Precombat]")
+ var.raiseDeadExpiresAt = GetTime() + 60
+ return true
+ end
+ end
+ -- * Death Grip
+ if ui.checked("Death Grip") and cast.able.deathGrip("target") and not unit.isDummy("target")
+ and unit.distance("target") > 8 and unit.distance("target") < 30
+ and (not unit.moving("target") or (unit.moving("target") and not unit.facing("target", "player")))
+ then
+ if cast.deathGrip("target") then
+ ui.debug("Casting Death Grip [Precombat]")
+ return true
+ end
+ end
+ -- * Howling Blast / Icy Touch
+ if (ui.mode.rotation == 3 or not cast.safe.howlingBlast("target","targetAOE",1,10)) and cast.able.icyTouch("target") then
+ if cast.icyTouch("target") then ui.debug("Casting Icy Touch [Precombat]") return true end
+ elseif cast.able.howlingBlast("target","targetAOE",1,10) then
+ if cast.howlingBlast("target","targetAOE",1,10) then ui.debug("Casting Howling Blast [Precombat]") return true end
+ end
+ -- * Start Attack
+ -- actions=auto_attack
+ if cast.able.autoAttack("target") and unit.distance("target") < 5 then
+ if cast.autoAttack("target") then
+ ui.debug("Casting Auto Attack [Precombat]")
+ return true
+ end
+ end
+ end
+ end
+end -- End Action List - PreCombat
+
+-- Action List - Combat
+actionList.Combat = function()
+ -- Check for combat
+ if unit.inCombat() and unit.valid("target") and not var.profileStop then
+ -- * Start Attack
+ -- actions=auto_attack
+ if cast.able.autoAttack(units.dyn5) and unit.distance(units.dyn5) < 5 then
+ if cast.autoAttack(units.dyn5) then
+ ui.debug("Casting Auto Attack [Combat]")
+ return true
+ end
+ end
+ -- * Death Grip
+ if ui.checked("Death Grip") and cast.able.deathGrip("target") and not unit.isDummy("target")
+ and unit.distance("target") > 8 and unit.distance("target") < 30
+ and (not unit.moving("target") or (unit.moving("target") and not unit.facing("target", "player")))
+ then
+ if cast.deathGrip("target") then
+ ui.debug("Casting Death Grip [Combat]")
+ return true
+ end
+ end
+ -- -- Cooldowns / Utility
+ -- -- * Anti-Magic Shell
+ -- -- antimagic_shell,damage=100000
+ -- if cast.able.antiMagicShell() then
+ -- if cast.antiMagicShell() then ui.debug("Casting Anti-Magic Shell") return true end
+ -- end
+ -- * Pillar of Frost
+ -- pillar_of_frost
+ if ui.alwaysCdAoENever("Pillar of Frost",3,#enemies.yards8) and cast.able.pillarOfFrost() then
+ if cast.pillarOfFrost() then ui.debug("Casting Pillar of Frost [Combat]") return true end
+ end
+ -- -- * Combat Potion usage
+ -- -- mogu_power_potion,if=target.time_to_die<=30|(target.time_to_die<=60&buff.pillar_of_frost.up)
+ -- if module and module.CombatPotionUp and unit.ttd and unit.ttd(units.dyn5) then
+ -- if unit.ttd(units.dyn5) <= 30 or (unit.ttd(units.dyn5) <= 60 and buff and buff.pillarOfFrost and buff.pillarOfFrost.exists()) then
+ -- module.CombatPotionUp()
+ -- end
+ -- end
+ -- * Empower Rune Weapon
+ -- empower_rune_weapon,if=target.time_to_die<=60&(buff.mogu_power_potion.up|buff.golemblood_potion.up)
+ if ui.alwaysCdAoENever("Empower Rune Weapon", 3, #enemies.yards5f) and cast.able.empowerRuneWeapon()
+ and runes() == 0 and runicPower() <= 75 and unit.ttd(units.dyn5) <= 60
+ then
+ if cast.empowerRuneWeapon() then ui.debug("Casting Empower Rune Weapon [Combat]") return true end
+ end
+ -- * Racials
+ -- if br.player and br.player.race then
+ -- if br.player.race == "Orc" and cast.able.bloodFury() then
+ -- if cast.bloodFury() then ui.debug("Casting Blood Fury") return true end
+ -- end
+ -- if br.player.race == "Troll" and cast.able.berserking() then
+ -- if cast.berserking() then ui.debug("Casting Berserking") return true end
+ -- end
+ -- if br.player.race == "BloodElf" and cast.able.arcaneTorrent() then
+ -- if cast.arcaneTorrent() then ui.debug("Casting Arcane Torrent") return true end
+ -- end
+ -- end
+ -- * Raise Dead
+ -- raise_dead
+ if ui.alwaysCdAoENever("Raise Dead", 3, #enemies.yards8) and cast.able.raiseDead() then
+ if cast.raiseDead() then
+ ui.debug("Casting Raise Dead [Combat]")
+ var.raiseDeadExpiresAt = GetTime() + 60
+ return true
+ end
+ end
+ -- * Run Action List: AOE
+ -- run_action_list,name=aoe,if=active_enemies>=3
+ if ui.useAOE(8,3) then
+ if actionList.AOE() then return true end
+ end
+ -- * Run Action List: Single Target
+ -- run_action_list,name=single_target,if=active_enemies<3
+ if ui.useST(8,3) then
+ if actionList.SingleTarget() then return true end
+ end
+ end -- End In Combat Rotation
+end -- End Action List - Combat
+
+----------------
+--- ROTATION ---
+----------------
+local function runRotation()
+ ---------------------
+ --- Define Locals ---
+ ---------------------
+ -- BR API Locals
+ buff = br.player.buff
+ cast = br.player.cast
+ cd = br.player.cd
+ debuff = br.player.debuff
+ enemies = br.player.enemies
+ module = br.player.module
+ runes = br.player.power.runes
+ runicPower = br.player.power.runicPower
+ spell = br.player.spell
+ talent = br.player.talent
+ ui = br.player.ui
+ unit = br.player.unit
+ units = br.player.units
+ use = br.player.use
+ var = br.player.variables
+
+ -- General Locals
+ -- Units
+ units.get(5) -- Makes a variable called, units.dyn5
+ units.get(30) -- Makes a variable called, units.dyn30
+ units.get(40)
+ -- Enemies
+ enemies.get(5) -- makes enemies.yards5
+ enemies.get(5, "player", false, true) -- makes enemies.yards5f
+ enemies.get(8)
+ enemies.get(15)
+ enemies.get(30)
+
+ if var.profileStop == nil then var.profileStop = false end
+ if var.raiseDeadExpiresAt == nil then var.raiseDeadExpiresAt = 0 end
+
+ -- ui.chatOverlay("Blood: " .. runes.blood() .. " | Frost: " .. runes.frost() .. " | Unholy: " .. runes.unholy()
+ -- .. " | Death: " .. runes.death())
+ -- ui.chatOverlay("Enemies (8y): " .. #enemies.yards8)
+
+ ---------------------
+ --- Begin Profile ---
+ ---------------------
+ -- Profile Stop | Pause
+ if not unit.inCombat() and not unit.exists("target") and var.profileStop then
+ var.profileStop = false
+ elseif (unit.inCombat() and var.profileStop == true) or ui.pause() or unit.mounted() or unit.flying() then
+ return true
+ else
+ ---------------------------------
+ --- Out Of Combat - Rotations ---
+ ---------------------------------
+ -----------------
+ --- Defensive ---
+ -----------------
+ if actionList.Defensive() then return true end
+ if ui.mode.rotation == 4 then return true end
+ ------------------
+ --- Pre-Combat ---
+ ------------------
+ if actionList.PreCombat() then return true end
+ -----------------
+ --- Interrupt ---
+ -----------------
+ if actionList.Interrupts() then return true end
+ -----------------------------
+ --- In Combat - Rotations ---
+ -----------------------------
+ if actionList.Combat() then return true end
+ end -- Pause
+end -- End runRotation
+local id = 251
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
+ name = rotationName,
+ toggles = createToggles,
+ options = createOptions,
+ run = runRotation,
+})
diff --git a/Rotations/Death Knight/Initial/InitialDeathKnight.lua b/Rotations/Death Knight/Initial/InitialDeathKnight.lua
index 03309b693d..2292200ab2 100644
--- a/Rotations/Death Knight/Initial/InitialDeathKnight.lua
+++ b/Rotations/Death Knight/Initial/InitialDeathKnight.lua
@@ -72,13 +72,13 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
-- Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupt Key Toggle
- br.ui:createDropdownWithout(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
-- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -251,7 +251,7 @@ local function runRotation()
unit = br.player.unit
units = br.player.units
-- General Locals
- var.haltProfile = (unit.inCombat() and var.profileStop) or unit.mounted() or br.pause() or ui.mode.rotation == 2
+ var.haltProfile = (unit.inCombat() and var.profileStop) or unit.mounted() or br.functions.misc:pause() or ui.mode.rotation == 2
-- Units
units.get(5) -- Makes a variable called, units.dyn5
units.get(40)
@@ -290,8 +290,9 @@ local function runRotation()
end -- Pause
end -- End runRotation
local id = 1455
-if br.rotations[id] == nil then br.rotations[id] = {} end
-br._G.tinsert(br.rotations[id], {
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Death Knight/Unholy/BrewUnholyDK.lua b/Rotations/Death Knight/Unholy/BrewUnholyDK.lua
index 1098269401..0e2e55d1a5 100644
--- a/Rotations/Death Knight/Unholy/BrewUnholyDK.lua
+++ b/Rotations/Death Knight/Unholy/BrewUnholyDK.lua
@@ -123,15 +123,15 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdown(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdown(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
--Cooldown Key Toggle
- br.ui:createDropdown(section, "Cooldown Mode", br.dropOptions.Toggle, 3)
+ br.ui:createDropdown(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 3)
--Defensive Key Toggle
- br.ui:createDropdown(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupts Key Toggle
- br.ui:createDropdown(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
-- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = {{
@@ -562,7 +562,7 @@ end
actionList.SingleTargetStandard = function()
if #enemies.yards5f == 1 then
- if br.getHP("target") <= 35 and unit.ttd("target") > 5 and not debuff.exists.soulReaper("target") and cast.able.soulReaper("target") then
+ if br.functions.unit:getHP("target") <= 35 and unit.ttd("target") > 5 and not debuff.exists.soulReaper("target") and cast.able.soulReaper("target") then
if cast.soulReaper("target") then ui.debug("STS.01: Soul Reaper") return true; end;
end
if talent.outbreak and
@@ -593,7 +593,7 @@ actionList.SingleTargetStandard = function()
end
actionList.GargoyleActive = function()
- if br.getHP("target") <= 35 and not debuff.exists.soulReaper("target") and cast.able.soulReaper("target") then
+ if br.functions.unit:getHP("target") <= 35 and not debuff.exists.soulReaper("target") and cast.able.soulReaper("target") then
if cast.soulReaper("target") then ui.debug("GARG.01: Soul Reaper") return true; end;
end
if cast.able.deathCoil("target") and (runicPower >= 30 or buff.suddenDoom.exists()) then
@@ -1037,9 +1037,10 @@ local function runRotation() -- This is the main profile loop, any below this po
end -- Pause
end -- End runRotation
local id = 252
+local expansion = br.isMOP
-- DO NOT EDIT ANYTHING BELOW THIS LINE, WILL BREAK PROFILE --
-if br.rotations[id] == nil then br.rotations[id] = {} end
-tinsert(br.rotations[id],{
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+tinsert(br.loader.rotations[id],{
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Demon Hunter/Havoc/HavocCuteOne.lua b/Rotations/Demon Hunter/Havoc/HavocCuteOne.lua
index a8b5848195..a20a924c5a 100644
--- a/Rotations/Demon Hunter/Havoc/HavocCuteOne.lua
+++ b/Rotations/Demon Hunter/Havoc/HavocCuteOne.lua
@@ -155,15 +155,15 @@ local function createOptions()
-- Toggle Key Options
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
-- Cooldown Key Toggle
- br.ui:createDropdownWithout(section, "Cooldown Mode", br.dropOptions.Toggle, 3)
+ br.ui:createDropdownWithout(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 3)
-- Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupts Key Toggle
- br.ui:createDropdownWithout(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
-- Mover Key Toggle
- br.ui:createDropdownWithout(section, "Mover Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Mover Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -242,7 +242,7 @@ actionList.Extras = function()
-- Dummy Test
if ui.checked("DPS Testing") then
if unit.exists("target") then
- if br.getCombatTime() >= (tonumber(ui.value("DPS Testing")) * 60) and unit.isDummy("target") then
+ if br.functions.combat:getCombatTime() >= (tonumber(ui.value("DPS Testing")) * 60) and unit.isDummy("target") then
br._G.StopAttack()
br._G.ClearTarget()
br._G.Print(tonumber(ui.value("DPS Testing")) .. " Minute Dummy Test Concluded - Profile Stopped")
@@ -336,7 +336,7 @@ actionList.Interrupts = function()
if ui.checked("Fel Eruption") and talent.felEruption then
for i = 1, #enemies.yards20 do
local thisUnit = enemies.yards20[i]
- if br.canInterrupt(thisUnit, ui.value("Interrupt At")) and cast.able.felEruption(thisUnit) then
+ if br.functions.spell:canInterrupt(thisUnit, ui.value("Interrupt At")) and cast.able.felEruption(thisUnit) then
if cast.felEruption(thisUnit) then
ui.debug("Casting Fel Eruption")
return true
@@ -348,7 +348,7 @@ actionList.Interrupts = function()
if ui.checked("Disrupt") then
for i = 1, #enemies.yards10 do
local thisUnit = enemies.yards10[i]
- if br.canInterrupt(thisUnit, ui.value("Interrupt At")) and cast.able.disrupt(thisUnit) then
+ if br.functions.spell:canInterrupt(thisUnit, ui.value("Interrupt At")) and cast.able.disrupt(thisUnit) then
if cast.disrupt(thisUnit) then
ui.debug("Casting Disrupt")
return true
@@ -360,7 +360,7 @@ actionList.Interrupts = function()
if ui.checked("Chaos Nova") then
for i = 1, #enemies.yards5 do
local thisUnit = enemies.yards5[i]
- if br.canInterrupt(thisUnit, ui.value("InterruptAt")) and cast.able.chaosNova(thisUnit) then
+ if br.functions.spell:canInterrupt(thisUnit, ui.value("InterruptAt")) and cast.able.chaosNova(thisUnit) then
if cast.chaosNova(thisUnit) then
ui.debug("Casting Chaos Nova [Int]")
return true
@@ -1377,7 +1377,7 @@ local function runRotation()
var.inRaid = unit.instance == "raid"
-- target_if=min:debuff.burning_wound.remains
var.lowestBurningWound = debuff.burningWound.lowest(5, "remain") or "target"
- var.solo = #br.friend == 1
+ var.solo = #br.engines.healingEngine.friend == 1
units.get(5)
units.get(8, true)
@@ -1460,8 +1460,9 @@ local function runRotation()
end --End Rotation Logic
end -- End runRotation
local id = 577
-if br.rotations[id] == nil then br.rotations[id] = {} end
-br._G.tinsert(br.rotations[id], {
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Demon Hunter/Initial/InitialDemonHunter.lua b/Rotations/Demon Hunter/Initial/InitialDemonHunter.lua
index d1d46c9604..e23e7abb8c 100644
--- a/Rotations/Demon Hunter/Initial/InitialDemonHunter.lua
+++ b/Rotations/Demon Hunter/Initial/InitialDemonHunter.lua
@@ -68,13 +68,13 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
--Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Cooldown Key Toggle
- br.ui:createDropdownWithout(section, "Cooldown Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 6)
-- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -233,7 +233,7 @@ local function runRotation()
units = br.player.units
use = br.player.use
-- General Locals
- var.haltProfile = (unit.inCombat() and var.profileStop) or br._G.IsMounted() or br.pause() or ui.mode.rotation == 2
+ var.haltProfile = (unit.inCombat() and var.profileStop) or br._G.IsMounted() or br.functions.misc:pause() or ui.mode.rotation == 2
-- Units
units.get(5) -- Makes a variable called, units.dyn5
units.get(40)
@@ -271,8 +271,9 @@ local function runRotation()
end -- Pause
end -- End runRotation
local id = 1456
-if br.rotations[id] == nil then br.rotations[id] = {} end
-br._G.tinsert(br.rotations[id], {
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Demon Hunter/Vengeance/VengeanceCuteOne.lua b/Rotations/Demon Hunter/Vengeance/VengeanceCuteOne.lua
index cc396252e0..12351da574 100644
--- a/Rotations/Demon Hunter/Vengeance/VengeanceCuteOne.lua
+++ b/Rotations/Demon Hunter/Vengeance/VengeanceCuteOne.lua
@@ -154,19 +154,19 @@ local function createOptions()
-- Toggle Key Options
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
-- Cooldown Key Toggle
- br.ui:createDropdownWithout(section, "Cooldown Mode", br.dropOptions.Toggle, 3)
+ br.ui:createDropdownWithout(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 3)
-- Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupts Key Toggle
- br.ui:createDropdownWithout(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
-- Mover Key Toggle
- br.ui:createDropdownWithout(section, "Mover Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Mover Mode", br.ui.dropOptions.Toggle, 6)
-- Tankbuster Toggle
- br.ui:createDropdownWithout(section, "Tankbuster Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Tankbuster Mode", br.ui.dropOptions.Toggle, 6)
-- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -245,13 +245,13 @@ actionList.Extras = function()
if br.lists.tankBuster[select(9, br._G.UnitCastingInfo("target"))] ~= nil then
if cd.demonSpikes.ready() and not debuff.fieryBrand.exists(thisUnit) then
if cast.demonSpikes() then
- br.addonDebug("[TANKBUST] Demon Spike")
+ br.functions.misc:addonDebug("[TANKBUST] Demon Spike")
return true
end
end
if cast.able.fieryBrand() and not buff.demonSpikes.exists() then
if cast.fieryBrand(thisUnit) then
- br.addonDebug("[TANKBUST] Fiery Brand")
+ br.functions.misc:addonDebug("[TANKBUST] Fiery Brand")
return true
end
end
@@ -1133,8 +1133,9 @@ local function runRotation()
end --End Rotation Logic
end -- End runRotation
local id = 581
-if br.rotations[id] == nil then br.rotations[id] = {} end
-br._G.tinsert(br.rotations[id], {
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Druid/Druid/Druid-TBC.lua b/Rotations/Druid/Druid/Druid-TBC.lua
new file mode 100644
index 0000000000..cc76027f97
--- /dev/null
+++ b/Rotations/Druid/Druid/Druid-TBC.lua
@@ -0,0 +1,1465 @@
+local rotationName = "CuteOne" -- Change to name of profile listed in options drop down
+
+---------------
+--- Toggles ---
+---------------
+local function createToggles() -- Define custom toggles
+ -- Rotation Button
+ local RotationModes = {
+ [1] = { mode = "Auto", value = 1, overlay = "Automatic Rotation", tip = "Swaps between Single and Multiple based on number of #enemies.yards8 in range.", highlight = 1, icon = br.player.spells.swipe },
+ [2] = { mode = "Multi", value = 2, overlay = "Multiple Target Rotation", tip = "Forces Multiple Target Rotation.", highlight = 0, icon = br.player.spells.swipe },
+ [3] = { mode = "Single", value = 3, overlay = "Single Target Rotation", tip = "Forces Single Target Rotation.", highlight = 0, icon = br.player.spells.shred },
+ [4] = { mode = "Off", value = 4, overlay = "DPS Rotation Disabled", tip = "Disable DPS Rotation", highlight = 0, icon = br.player.spells.healingTouch }
+ };
+ br.ui:createToggle(RotationModes, "Rotation", 1, 0)
+ -- Cooldown Button
+ local CooldownModes = {
+ [1] = { mode = "Auto", value = 1, overlay = "Cooldowns Automated", tip = "Automatic Cooldowns - Boss Detection.", highlight = 1, icon = br.player.spells.tigersFury },
+ [2] = { mode = "On", value = 2, overlay = "Cooldowns Enabled", tip = "Cooldowns used regardless of target.", highlight = 0, icon = br.player.spells.tigersFury },
+ [3] = { mode = "Off", value = 3, overlay = "Cooldowns Disabled", tip = "No Cooldowns will be used.", highlight = 0, icon = br.player.spells.tigersFury }
+ };
+ br.ui:createToggle(CooldownModes, "Cooldown", 2, 0)
+ -- Defensive Button
+ local DefensiveModes = {
+ [1] = { mode = "On", value = 1, overlay = "Defensive Enabled", tip = "Includes Defensive Cooldowns.", highlight = 1, icon = br.player.spells.healingTouch },
+ [2] = { mode = "Off", value = 2, overlay = "Defensive Disabled", tip = "No Defensives will be used.", highlight = 0, icon = br.player.spells.healingTouch }
+ };
+ br.ui:createToggle(DefensiveModes, "Defensive", 3, 0)
+ -- Interrupt Button
+ local InterruptModes = {
+ [1] = { mode = "On", value = 1, overlay = "Interrupts Enabled", tip = "Includes Basic Interrupts.", highlight = 1, icon = br.player.spells.bash },
+ [2] = { mode = "Off", value = 2, overlay = "Interrupts Disabled", tip = "No Interrupts will be used.", highlight = 0, icon = br.player.spells.bash }
+ };
+ br.ui:createToggle(InterruptModes, "Interrupt", 4, 0)
+ -- Form Button
+ local FormModes = {
+ [1] = { mode = "Caster", value = 1, overlay = "Caster Form", tip = "Will force and use Caster Form", highlight = 1, icon = br.player.spells.moonkinForm },
+ [2] = { mode = "Cat", value = 2, overlay = "Cat Form", tip = "Will force and use Cat Form", highlight = 0, icon = br.player.spells.catForm },
+ [3] = { mode = "Bear", value = 3, overlay = "Bear Form", tip = "Will force and use Bear Form", highlight = 0, icon = br.player.spells.bearForm }
+ };
+ br.ui:createToggle(FormModes, "Forms", 5, 0)
+ -- Prowl Button
+ local ProwlModes = {
+ [1] = { mode = "On", value = 1, overlay = "Prowl Enabled", tip = "Rotation will use Prowl", highlight = 1, icon = br.player.spells.prowl },
+ [2] = { mode = "Off", value = 2, overlay = "Prowl Disabled", tip = "Rotation will not use Prowl", highlight = 0, icon = br.player.spells.prowl }
+ };
+ br.ui:createToggle(ProwlModes, "Prowl", 6, 0)
+end
+---------------
+--- OPTIONS ---
+---------------
+local function createOptions()
+ local optionTable
+
+ local function rotationOptions()
+ local section
+ -----------------------
+ --- GENERAL OPTIONS ---
+ -----------------------
+ section = br.ui:createSection(br.ui.window.profile, "General")
+ br.ui:createSpinnerWithout(section, "Shift Wait Time", 2, 0, 5, 1,
+ "|cffFFFFFFTime in seconds the profile will wait while moving to shift. Combat is instant!")
+ br.ui:createCheckbox(section, "Behind Debug", "|cffFFFFFFDebug behind/facing checks for Shred/Ravage.")
+ -- Powershifting
+ br.ui:createCheckbox(section, "Powershifting", "|cffFFFFFFFeral DPS In-Form Shifting for Energy")
+ -- Cower
+ br.ui:createCheckbox(section, "Cower", "|cffFFFFFFUse Cower (20 energy) to drop threat when being attacked while a tank is alive and nearby")
+ br.ui:checkSectionState(section)
+ --------------------------
+ --- FORM MANAGEMENT ---
+ --------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Form Management")
+ -- Aquatic Form
+ br.ui:createCheckbox(section, "Aquatic Form", "|cffFFFFFFEnable/Disable Using Aquatic Form")
+ -- Travel Form
+ br.ui:createCheckbox(section, "Travel Form", "|cffFFFFFFEnable/Disable Using Travel Form")
+ -- Last Form
+ br.ui:createCheckbox(section, "Last Form", "|cffFFFFFFEnable/Disable Returning to Last Form")
+ -- Energy Threshold
+ br.ui:createSpinner(section, "Energy", 30, 1, 100, 1, "|cffFFFFFFCat Form Energy Threshold to Break Form")
+ -- Rage Threshold
+ br.ui:createSpinner(section, "Rage", 10, 1, 100, 1, "|cffFFFFFFBear Form Rage Threshold to Break Form")
+ br.ui:checkSectionState(section)
+ ----------------------
+ --- BLEED OPTIONS ---
+ ----------------------
+ section = br.ui:createSection(br.ui.window.profile, "Bleeds")
+ -- Rake
+ br.ui:createCheckbox(section, "Rake", "|cffFFFFFFEnable/Disable Using Rake")
+ -- Rip
+ br.ui:createCheckbox(section, "Rip", "|cffFFFFFFEnable/Disable Using Rip")
+ br.ui:checkSectionState(section)
+ ---------------------
+ --- BUFF OPTIONS ---
+ ---------------------
+ section = br.ui:createSection(br.ui.window.profile, "Buffs")
+ -- Self-Innervate
+ br.ui:createCheckbox(section, "Self-Innervate", "|cffFFFFFFEnable/Disable Using Innervate on Self")
+ -- Mark of the Wild
+ br.ui:createDropdown(section, "Mark of the Wild",
+ { "|cffFFFFFFPlayer", "|cffFFFFFFTarget", "|cffFFFFFFMouseover", "|cffFFFFFFFocus", "|cffFFFFFFGroup" }, 1,
+ "|cffFFFFFFSet how to use Mark of the Wild")
+ -- Omen of Clarity
+ br.ui:createDropdown(section, "Omen of Clarity",
+ { "|cffFFFFFFAny", "|cffFFFFFFFocus DPS", "|cffFFFFFFFocus Healing", "|cffFFFFFFNone" }, 1,
+ "|cffFFFFFFSelect how to use Omen of Clarity procs")
+ -- Thorns
+ br.ui:createDropdown(section, "Thorns",
+ { "|cffFFFFFFPlayer", "|cffFFFFFFTarget", "|cffFFFFFFMouseover", "|cffFFFFFFFocus", "|cffFFFFFFGroup" }, 1,
+ "|cffFFFFFFSet how to use Thorns")
+ -- Tiger's Fury
+ br.ui:createCheckbox(section, "Tiger's Fury", "|cffFFFFFFEnable/Disable Using Tiger's Fury")
+ br.ui:checkSectionState(section)
+ -------------------------
+ --- DEFENSIVE OPTIONS ---
+ -------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Defensive")
+ -- Self-Heal In Group
+ br.ui:createCheckbox(section, "Self Heal In Group", "|cffFFFFFFEnable/Disable Healing Self while Grouped")
+ -- Healing Touch
+ br.ui:createSpinner(section, "Healing Touch", 40, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ -- Regrowth
+ br.ui:createSpinner(section, "Regrowth", 60, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ -- Rejuvenation
+ br.ui:createSpinner(section, "Rejuvenation", 80, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ -- Barkskin
+ br.ui:createSpinner(section, "Barkskin", 50, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ -- Abolish Poison
+ br.ui:createCheckbox(section, "Abolish Poison", "|cffFFFFFFEnable/Disable Using Abolish Poison")
+ -- Cure Poison
+ br.ui:createCheckbox(section, "Cure Poison", "|cffFFFFFFEnable/Disable Using Cure Poison")
+ -- Remove Curse
+ br.ui:createCheckbox(section, "Remove Curse", "|cffFFFFFFEnable/Disable Using Remove Curse")
+ -- Entangling Roots
+ br.ui:createCheckbox(section, "Entangling Roots", "|cffFFFFFFEnable/Disable Using Entangling Roots on fleeing enemies")
+ -- Faerie Fire
+ br.ui:createCheckbox(section, "Faerie Fire", "|cffFFFFFFEnable/Disable Using Faerie Fire/Faerie Fire Feral")
+ -- Nature's Grasp
+ br.ui:createCheckbox(section, "Nature's Grasp", "|cffFFFFFFEnable/Disable Using Nature's Grasp")
+ -- -- Health Potion
+ -- br.ui:createCheckbox(section, "Health Potion", "|cffFFFFFFEnable/Disable Using Health Potion")
+ -- -- Mana Potion
+ -- br.ui:createCheckbox(section, "Mana Potion", "|cffFFFFFFEnable/Disable Using Mana Potion")
+ -- Bash
+ br.ui:createSpinner(section, "Bash", 50, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ br.ui:checkSectionState(section)
+ -------------------------
+ --- Interrupt Options ---
+ -------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Interrupts")
+ -- Bash
+ br.ui:createCheckbox(section, "Bash - Int")
+ -- Maim
+ br.ui:createCheckbox(section, "Maim")
+ -- Interrupt Percentage
+ br.ui:createSpinnerWithout(section, "Interrupt At", 0, 0, 95, 5,
+ "|cffFFFFFFCast Percent to Cast At (0 is random)")
+ br.ui:checkSectionState(section)
+ ----------------------
+ --- TOGGLE OPTIONS ---
+ ----------------------
+ section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
+ -- Single/Multi Toggle
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
+ -- Cooldown Key Toggle
+ br.ui:createDropdownWithout(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 3)
+ -- Defensive Key Toggle
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
+ -- Interrupt Key Toggle
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
+ -- Forms Key Toggle
+ br.ui:createDropdownWithout(section, "Forms Mode", br.ui.dropOptions.Toggle, 6)
+ -- Prowl Key Toggle
+ br.ui:createDropdownWithout(section, "Prowl Mode", br.ui.dropOptions.Toggle, 6)
+ br.ui:checkSectionState(section)
+ end
+ optionTable = { {
+ [1] = "Rotation Options",
+ [2] = rotationOptions,
+ } }
+ return optionTable
+end
+
+--------------
+--- Locals ---
+--------------
+-- BR API Locals - Many of these are located from System/API, this is a sample of commonly used ones but no all inclusive
+local buff
+local cast
+local cd
+local comboPoints
+local debuff
+local enemies
+local energy
+local rage
+local mana
+local module
+local ui
+local unit
+local units
+local spell
+local var = {}
+-- General Locals - Common Non-BR API Locals used in profiles
+local haltProfile
+local profileStop
+-- Profile Specific Locals - Any custom to profile locals
+local actionList = {}
+-- local fbMaxEnergy
+local formCost
+local formValue
+local movingTimer
+local lastForm
+local powershiftReady = false
+local powershiftArmedAt = 0
+-- local lastFormBuff = false
+-- local freeDPS
+-- local freeHeal
+local keepAquatic
+
+-- local noShapeshiftPower
+-- local needsHealing
+
+-----------------
+--- Functions --- -- List all profile specific custom functions here
+-----------------
+---
+-- estimate time (s) until next energy 'tick' using regen (energy/sec)
+local function timeToNextEnergyTick()
+ local regen = br.player.power.energy.regen() or 0
+ if regen > 0 then
+ return 1 / regen
+ end
+ return 99
+end
+
+-- AutoProwl
+-- local function autoProwl()
+-- if not unit.inCombat() and not buff.prowl.exists() then
+-- if #enemies.yards20 > 0 then return true end
+-- if #enemies.yards20nc > 0 then
+-- for i = 1, #enemies.yards20nc do
+-- local thisUnit = enemies.yards20nc[i]
+-- local threatRange = math.max((20 + (unit.level(thisUnit) - unit.level())), 5)
+-- local react = unit.reaction(thisUnit) or 10
+-- if unit.distance(thisUnit) < threatRange and (react < 4 or (unit.isUnit("target", thisUnit) and react == 4))
+-- and unit.enemy(thisUnit) and unit.canAttack(thisUnit) and not unit.deadOrGhost(thisUnit)
+-- then
+-- return true
+-- end
+-- end
+-- end
+-- -- if unit.isDummy("target") and unit.distance("target") < 20 then return true end
+-- end
+-- return false
+-- end
+
+-- Ferocious Bite Finish
+local function ferociousBiteFinish(thisUnit)
+ local desc = br._G.C_Spell.GetSpellDescription(spell.ferociousBite.id()) or ""
+ if desc == "" then return false end
+
+ local finishHim = false
+ if thisUnit == nil then thisUnit = "target" end
+ if comboPoints() <= 0 or unit.isDummy(thisUnit) then return false end
+
+ local comboStart = desc:find(" " .. comboPoints() .. " ", 1, true)
+ if comboStart == nil then return false end
+
+ local damageList = desc:sub(comboStart + 2)
+ local colonPos = damageList:find(": ", 1, true)
+ if colonPos == nil then return false end
+ damageList = damageList:sub(colonPos + 2)
+
+ local spacePos = damageList:find(" ", 1, true) or (#damageList + 1)
+ local token = damageList:sub(1, spacePos - 1):gsub(",", "")
+
+ local low = token:match("^(%d+)%-%d+$") or token:match("^(%d+)$")
+ local lower = tonumber(low)
+ if lower == nil then return false end
+
+ finishHim = lower >= (unit.health(thisUnit) or 0)
+ return finishHim
+end
+
+-- Time Moving
+local function timeMoving()
+ if movingTimer == nil then movingTimer = br._G.GetTime() end
+ if not unit.moving() then
+ movingTimer = br._G.GetTime()
+ end
+ return br._G.GetTime() - movingTimer
+end
+
+-- Behind check (nil-safe)
+-- Prefer the engine API so all profiles share the same fix/behavior.
+local function isBehind(thisUnit, otherUnit, degrees)
+ if unit == nil or unit.isBehind == nil then return false end
+ local result = unit.isBehind(thisUnit, otherUnit, degrees)
+ if result == nil or result == false then
+ if ui and ui.checked and ui.checked("Behind Debug") then
+ ui.debug(string.format(
+ "isBehind: FALSE (angle missing or not behind) | thisUnit=%s | otherUnit=%s | degrees=%s",
+ tostring(thisUnit), tostring(otherUnit), tostring(degrees)
+ ))
+ end
+ return false
+ end
+ return true
+end
+
+local function safeCancelForm(outOfFormSpellRef, reason, treatOutSpellAsFree)
+ treatOutSpellAsFree = false -- Not currently used, but can be used to allow ignoring mana cost of out of form spell when checking if we can break form
+ if unit.exists("target") and unit.friend("target") then
+ -- ui.debug("Skip Cancel Form [" .. tostring(reason) .. "] (friendly target)")
+ return false
+ end
+
+ if unit == nil or unit.canCancelFormFor == nil or unit.formBreakManaRequired == nil then
+ -- Fallback (shouldn't happen during normal profile execution)
+ if buff.catForm.exists() or buff.bearForm.exists() or buff.travelForm.exists() or buff.aquaticForm.exists() then
+ if unit and unit.cancelForm then
+ unit.cancelForm()
+ else
+ br._G.RunMacroText("/CancelForm")
+ end
+ ui.debug("Cancel Form [" .. tostring(reason) .. "]")
+ return true
+ end
+ return false
+ end
+
+ if not unit.canCancelFormFor(outOfFormSpellRef, lastForm, treatOutSpellAsFree) then
+ local requiredMana, currentMana = unit.formBreakManaRequired(outOfFormSpellRef, lastForm, treatOutSpellAsFree)
+ -- ui.debug("Skip Cancel Form [" .. tostring(reason) .. "] (mana " .. tostring(currentMana) .. " < need " .. tostring(requiredMana) .. ")")
+ return false
+ end
+
+ if unit and unit.cancelForm then
+ unit.cancelForm()
+ else
+ br._G.RunMacroText("/CancelForm")
+ end
+ ui.debug("Cancel Form [" .. tostring(reason) .. "]")
+ return true
+end
+
+-- In Aggro Range Check
+local function inAggroRange(offset)
+ offset = offset or 5
+ for i = 1, #enemies.yards20nc do
+ local thisUnit = enemies.yards20nc[i]
+ local threatRange = math.max((20 + (unit.level(thisUnit) - unit.level())), 5) + offset
+ --local react = unit.reaction(thisUnit) or 10
+ if unit.distance(thisUnit) < threatRange and not unit.friend(thisUnit) then--(react < 3 or (unit.isUnit("target", thisUnit) and react == 4)) then
+ -- ui.debug("Unit in Aggro Range: " .. unit.name(thisUnit) .. " at distance " .. tostring(unit.distance(thisUnit)) .. " with threat range " .. tostring(threatRange))
+ return true
+ end
+ end
+ return false
+end
+
+local getBuffUnitOption = function(option)
+ local thisTar = ui.value(option)
+ local thisUnit
+ if thisTar == 1 then
+ thisUnit = "player"
+ end
+ if thisTar == 2 then
+ thisUnit = "target"
+ if not unit.exists(thisUnit) then
+ thisUnit = "player"
+ end
+ end
+ if thisTar == 3 then
+ thisUnit = "mouseover"
+ if not unit.exists(thisUnit) then
+ thisUnit = "player"
+ end
+ end
+ if thisTar == 4 then
+ thisUnit = "focus"
+ if not unit.exists(thisUnit) then
+ thisUnit = "player"
+ end
+ end
+ if thisTar == 5 then
+ thisUnit = "player"
+ if #br.engines.healingEngine.friend > 1 then
+ for i = 1, #br.engines.healingEngine.friend do
+ local nextUnit = br.engines.healingEngine.friend[i].unit
+ local needsBuff = false
+ if option == "Mark of the Wild" then
+ needsBuff = buff.markOfTheWild.refresh(nextUnit)
+ elseif option == "Thorns" then
+ needsBuff = buff.thorns.refresh(nextUnit)
+ end
+ if needsBuff and unit.distance(nextUnit) < 40 then
+ thisUnit = nextUnit
+ break
+ end
+ end
+ end
+ end
+ return thisUnit
+end
+
+-- Keep Aquatic Form while swimming unless danger/enemy target.
+keepAquatic = function()
+ return ui.checked("Aquatic Form")
+ and br._G.IsSwimming()
+ and buff.aquaticForm.exists()
+ and not inAggroRange()
+ and (not unit.exists("target") or unit.friend("target"))
+end
+
+-- Safe to Cower: only drop threat if a Warrior or Paladin is alive and within range.
+-- TBC has no role system, so we use class as a proxy for reliable tank presence.
+-- If no such player is nearby, keeping aggro on ourselves is safer than handing it to a healer.
+local COWER_SAFE_TANK_CLASS = { WARRIOR = true, PALADIN = true }
+local function safeToCower(range)
+ range = range or 40
+ if #br.engines.healingEngine.friend < 2 then return false end
+ for i = 1, #br.engines.healingEngine.friend do
+ local f = br.engines.healingEngine.friend[i]
+ if not unit.isUnit(f.unit, "player")
+ and not unit.deadOrGhost(f.unit)
+ and unit.distance(f.unit) < range
+ then
+ local _, classTag = br._G.UnitClass(f.unit)
+ if classTag and COWER_SAFE_TANK_CLASS[classTag] then
+ return true
+ end
+ end
+ end
+ return false
+end
+
+--------------------
+--- Action Lists ---
+--------------------
+
+-- Action List - Extra
+actionList.Extra = function()
+ -- Mark of the Wild
+ if ui.checked("Mark of the Wild") and not unit.flying() and not buff.prowl.exists()
+ and mana() > cast.cost.markOfTheWild() + formCost
+ then
+ var.markUnit = getBuffUnitOption("Mark of the Wild")
+ if cast.able.markOfTheWild(var.markUnit) and buff.markOfTheWild.refresh(var.markUnit)
+ and not unit.inCombat() and not unit.resting() and unit.distance(var.markUnit) < 40
+ and (var.markUnit == "player" or unit.player(var.markUnit)) and unit.friend(var.markUnit)
+ and not buff.giftOfTheWild.exists(var.markUnit)
+ then
+ -- Cancel form if needed to buff
+ local needsCancel = buff.catForm.exists() or buff.bearForm.exists() or buff.travelForm.exists() or buff.aquaticForm.exists()
+ if needsCancel then
+ if safeCancelForm(spell.markOfTheWild, "Mark of the Wild") then
+ return true
+ end
+ else
+ if cast.markOfTheWild(var.markUnit) then
+ ui.debug("Casting Mark of the Wild")
+ return true
+ end
+ end
+ end
+ end
+ -- Thorns
+ if ui.checked("Thorns") and not unit.flying() and not buff.prowl.exists() and mana() > cast.cost.thorns() + formCost then
+ var.thornUnit = getBuffUnitOption("Thorns")
+ if cast.able.thorns(var.thornUnit) and buff.thorns.refresh(var.thornUnit)
+ and not unit.inCombat() and not unit.resting() and unit.distance(var.thornUnit) < 40
+ and (var.thornUnit == "player" or unit.player(var.thornUnit))
+ then
+ -- Cancel form if needed to buff
+ local needsCancel = buff.catForm.exists() or buff.bearForm.exists() or buff.travelForm.exists() or buff.aquaticForm.exists()
+ if needsCancel then
+ if safeCancelForm(spell.thorns, "Thorns") then
+ return true
+ end
+ else
+ if cast.thorns(var.thornUnit) then
+ ui.debug("Casting Thorns")
+ return true
+ end
+ end
+ end
+ end
+ -- Omen of Clarity
+ if ui.value("Omen of Clarity") ~= 4 and not unit.flying() and not unit.inCombat() and not unit.resting() and not buff.prowl.exists()
+ and mana() > cast.cost.omenOfClarity() + formCost
+ then
+ if cast.able.omenOfClarity("player") and buff.omenOfClarity.refresh("player") then
+ -- Cancel form if needed to buff
+ local needsCancel = buff.catForm.exists() or buff.bearForm.exists() or buff.travelForm.exists() or buff.aquaticForm.exists()
+ if needsCancel then
+ if safeCancelForm(spell.omenOfClarity, "Omen of Clarity") then
+ return true
+ end
+ else
+ if cast.omenOfClarity("player") then
+ ui.debug("Casting Omen of Clarity")
+ return true
+ end
+ end
+ end
+ end
+end -- End Action List - Extra
+
+-- Action List - Defensive
+actionList.Defensive = function()
+ if ui.useDefensive() then
+ -- Abolish Poison
+ if ui.checked("Abolish Poison") and cast.able.abolishPoison("player")
+ and not buff.abolishPoison.exists("player") and cast.dispel.abolishPoison("player")
+ and mana() > cast.cost.abolishPoison() + formCost
+ then
+ if buff.catForm.exists() or buff.bearForm.exists() then
+ if safeCancelForm(spell.abolishPoison, "Abolish Poison") then
+ return true
+ end
+ else
+ if cast.abolishPoison("player") then
+ ui.debug("Casting Abolish Poison")
+ return true
+ end
+ end
+ end
+ -- Cure Poison
+ if ui.checked("Cure Poison") and cast.able.curePoison("player")
+ and (not ui.checked("Abolish Poison") --[[or not spell.abolishPoison.known()]]) and cast.dispel.curePoison("player")
+ and mana() > cast.cost.curePoison() + formCost
+ then
+ if buff.catForm.exists() or buff.bearForm.exists() then
+ if safeCancelForm(spell.curePoison, "Cure Poison") then
+ return true
+ end
+ else
+ if cast.curePoison("player") then
+ ui.debug("Casting Cure Poison")
+ return true
+ end
+ end
+ end
+ -- Remove Curse
+ if ui.checked("Remove Curse") and cast.able.removeCurse("player")
+ and not unit.inCombat() and cast.dispel.removeCurse("player")
+ and mana() > cast.cost.removeCurse() + formCost
+ then
+ if buff.catForm.exists() or buff.bearForm.exists() then
+ if safeCancelForm(spell.removeCurse, "Remove Curse") then
+ return true
+ end
+ else
+ if cast.removeCurse("player") then
+ ui.debug("Casting Remove Curse")
+ return true
+ end
+ end
+ end
+ -- Barkskin
+ if ui.checked("Barkskin") and cast.able.barkskin()
+ and unit.hp() <= ui.value("Barkskin") and unit.inCombat()
+ and mana() > cast.cost.barkskin() + formCost
+ then
+ if cast.barkskin() then
+ ui.debug("Casting Barkskin")
+ return true
+ end
+ end
+ -- Entangling Roots (fleeing enemies)
+ if ui.checked("Entangling Roots") and cast.able.entanglingRoots("target")
+ and unit.exists("target") and not unit.facing("target", "player") and unit.moving("target")
+ and unit.valid("target") and unit.hp("target") < 100 and not debuff.entanglingRoots.exists("target")
+ and unit.distance("target") > 8 and unit.inCombat()
+ and mana() > cast.cost.entanglingRoots() + formCost
+ then
+ if buff.catForm.exists() or buff.bearForm.exists() then
+ if safeCancelForm(spell.entanglingRoots, "Entangling Roots", buff.clearcasting.exists()) then
+ return true
+ end
+ else
+ if cast.entanglingRoots("target") then
+ ui.debug("Casting Entangling Roots")
+ return true
+ end
+ end
+ end
+ -- Faerie Fire (Caster)
+ if ui.checked("Faerie Fire") and cast.able.faerieFire("target") and not buff.prowl.exists() and formValue == 1
+ and unit.exists("target") and unit.enemy("target") and unit.canAttack("target") and not debuff.faerieFire.exists("target")
+ and not (buff.catForm.exists() or buff.bearForm.exists())
+ and unit.distance("target") > 8 and mana() > cast.cost.faerieFire() + formCost
+ then
+ if cast.faerieFire("target") then
+ ui.debug("Casting Faerie Fire")
+ return true
+ end
+ end
+ -- Nature's Grasp
+ if ui.checked("Nature's Grasp") and cast.able.naturesGrasp()
+ and not buff.naturesGrasp.exists("player") and unit.inCombat()
+ and #enemies.yards10 > 0 and mana() > cast.cost.naturesGrasp() + formCost
+ then
+ if buff.catForm.exists() or buff.bearForm.exists() then
+ if safeCancelForm(spell.naturesGrasp, "Nature's Grasp", buff.clearcasting.exists()) then
+ return true
+ end
+ else
+ if cast.naturesGrasp("player") then
+ ui.debug("Casting Nature's Grasp")
+ return true
+ end
+ end
+ end
+ -- Regrowth
+ if ui.checked("Regrowth") and cast.able.regrowth("player") and not cast.current.regrowth() and not unit.moving()
+ and unit.hp() <= ui.value("Regrowth") and not buff.regrowth.exists("player")
+ and (buff.clearcasting.exists() or mana() > cast.cost.regrowth() + formCost)
+ and (not unit.inGroup() or ui.checked("Self Heal In Group"))
+ then
+ if buff.catForm.exists() or buff.bearForm.exists() then
+ if safeCancelForm(spell.regrowth, "Regrowth", buff.clearcasting.exists()) then
+ return true
+ end
+ else
+ if cast.regrowth("player") then
+ ui.debug("Casting Regrowth")
+ return true
+ end
+ end
+ end
+ -- Rejuvenation
+ if ui.checked("Rejuvenation") and not cast.current.rejuvenation("target") and mana() > cast.cost.rejuvenation() + formCost then
+ if cast.able.rejuvenation("target") and unit.friend("target") and unit.hp("target") <= ui.value("Rejuvenation")
+ and unit.player("target") and buff.rejuvenation.refresh("target")
+ then
+ if buff.catForm.exists() or buff.bearForm.exists() then
+ if safeCancelForm(spell.rejuvenation, "Rejuvenation", buff.clearcasting.exists()) then
+ return true
+ end
+ else
+ ui.debug("Attempting to cast Rejuvenation on " .. unit.name("target"))
+ if cast.rejuvenation("target") then
+ return true
+ end
+ end
+ end
+ if cast.able.rejuvenation("player") and not unit.friend("target") and unit.hp() <= ui.value("Rejuvenation")
+ and buff.rejuvenation.refresh("player") and not buff.clearcasting.exists()
+ and (unit.solo() or ui.checked("Self Heal In Group"))
+ then
+ if buff.catForm.exists() or buff.bearForm.exists() then
+ if safeCancelForm(spell.rejuvenation, "Rejuvenation", buff.clearcasting.exists()) then
+ return true
+ end
+ else
+ if cast.rejuvenation("player") then
+ ui.debug("Casting Rejuvenation on " .. unit.name("player"))
+ return true
+ end
+ end
+ end
+ end
+ -- Healing Touch
+ if ui.checked("Healing Touch") and not cast.current.healingTouch() and not unit.moving()
+ and (buff.clearcasting.exists() or mana() > cast.cost.healingTouch() + formCost)
+ and cast.timeSinceLast.healingTouch() > unit.gcd(true)
+ then
+ if cast.able.healingTouch("target") and unit.friend("target") and unit.hp("target") <= ui.value("Healing Touch") and unit.player("target") then
+ if buff.catForm.exists() or buff.bearForm.exists() then
+ if safeCancelForm(spell.healingTouch, "Healing Touch", buff.clearcasting.exists()) then
+ return true
+ end
+ else
+ if cast.healingTouch("target") then
+ ui.debug("Casting Healing Touch on " .. unit.name("target"))
+ return true
+ end
+ end
+ end
+ if cast.able.healingTouch("player") and not unit.friend("target") and unit.hp() <= ui.value("Healing Touch")
+ and (not unit.inGroup() or ui.checked("Self Heal In Group"))
+ then
+ if buff.catForm.exists() or buff.bearForm.exists() then
+ if safeCancelForm(spell.healingTouch, "Healing Touch", buff.clearcasting.exists()) then
+ return true
+ end
+ else
+ if cast.healingTouch("player") then
+ ui.debug("Casting Healing Touch on " .. unit.name("player"))
+ return true
+ end
+ end
+ end
+ end
+ -- -- Health Potion
+ -- if ui.checked("Health Potion") and unit.canUseItem(5512)
+ -- and unit.hp() < ui.value("Rejuvenation")
+ -- and (not unit.inGroup() or ui.checked("Self Heal In Group"))
+ -- then
+ -- if buff.catForm.exists() or buff.bearForm.exists() then
+ -- br._G.RunMacroText("/CancelForm")
+ -- ui.debug("Cancel Form for Health Potion")
+ -- return true
+ -- end
+ -- if unit.useItem(5512) then
+ -- ui.debug("Using Health Potion")
+ -- return true
+ -- end
+ -- end
+ -- -- Mana Potion
+ -- if ui.checked("Mana Potion") and unit.canUseItem(2455)
+ -- and mana.percent() < 30
+ -- then
+ -- if buff.catForm.exists() or buff.bearForm.exists() then
+ -- br._G.RunMacroText("/CancelForm")
+ -- ui.debug("Cancel Form for Mana Potion")
+ -- return true
+ -- end
+ -- if unit.useItem(2455) then
+ -- ui.debug("Using Mana Potion")
+ -- return true
+ -- end
+ -- end
+ -- Bash (low HP defensive stun)
+ if ui.checked("Bash") and unit.hp() <= ui.value("Bash") then
+ if (buff.bearForm.exists() or buff.direBearForm.exists()) and cast.able.bash("target") then
+ if cast.bash("target") then
+ ui.debug("Casting Bash [Defensive]")
+ return true
+ end
+ end
+ end
+ end
+end -- End Action List - Defensive
+
+-- * Action List - Interrupts
+actionList.Interrupts = function()
+ if ui.useInterrupt() and ui.delay("Interrupts", unit.gcd(true))
+ -- and not (buff.prowl.exists() or buff.shadowmeld.exists())
+ then
+ local thisUnit
+ -- * Bash
+ if ui.checked("Bash - Int") then
+ for i = 1, #enemies.yards5f do
+ thisUnit = enemies.yards5f[i]
+ if unit.interruptable(thisUnit, ui.value("Interrupt At")) then
+ -- Bash is Bear Form only in Classic; shift first if needed.
+ if not buff.bearForm.exists() and cast.able.bearForm() then
+ if cast.bearForm() then
+ ui.debug("Shifting to Bear Form for Bash interrupt")
+ return true
+ end
+ end
+ if (buff.bearForm.exists() or buff.direBearForm.exists()) and cast.able.bash(thisUnit) then
+ if cast.bash(thisUnit) then
+ ui.debug("Casting Bash on " .. unit.name(thisUnit))
+ return true
+ end
+ end
+ end
+ end
+ end
+ -- * Maim
+ if ui.checked("Maim") and buff.catForm.exists() and comboPoints() > 0 and energy() >= 35 then
+ for i = 1, #enemies.yards5f do
+ thisUnit = enemies.yards5f[i]
+ if unit.interruptable(thisUnit, ui.value("Interrupt At")) then
+ if cast.able.maim(thisUnit) then
+ if cast.maim(thisUnit) then
+ ui.debug("Casting Maim on " .. unit.name(thisUnit))
+ return true
+ end
+ end
+ end
+ end
+ end
+ end -- End useInterrupts check
+end -- End Action List - Interrupts
+
+-- Action List - Cooldowns
+actionList.Cooldowns = function()
+ if not buff.prowl.exists() and unit.distance("target") < 5 then
+ -- * Module- Basic Trinkets
+ -- use_items
+ module.BasicTrinkets()
+ -- Innervate
+ if ui.checked("Self-Innervate") and ui.useCDs()
+ and cast.able.innervate() and mana.percent() < 30
+ then
+ if cast.innervate("player") then
+ ui.debug("Casting Innervate")
+ return true
+ end
+ end
+ end
+end -- End Action List - Cooldowns
+
+-- Action List - Form Management
+actionList.FormManagement = function()
+ -- Aquatic Form
+ if ui.checked("Aquatic Form") and not unit.inCombat() and cast.able.aquaticForm()
+ and br._G.IsSwimming() and not buff.aquaticForm.exists() and not buff.prowl.exists()
+ and unit.moving() and timeMoving() > 2 and not inAggroRange()
+ then
+ if buff.catForm.exists() or buff.bearForm.exists() or buff.travelForm.exists() then
+ if safeCancelForm(spell.aquaticForm, "Swimming") then
+ return true
+ end
+ end
+ if cast.aquaticForm() then
+ ui.debug("Casting Aquatic Form")
+ return true
+ end
+ end
+
+ -- Travel Form
+ if ui.checked("Travel Form") and not unit.inCombat() and not br._G.IsMounted()
+ and not br._G.IsSwimming() and not br._G.IsFalling() and cast.able.travelForm()
+ and not buff.travelForm.exists() and not buff.prowl.exists()
+ and unit.moving() and timeMoving() > 2 and not inAggroRange()
+ then
+ if buff.catForm.exists() or buff.bearForm.exists() then
+ if safeCancelForm(spell.travelForm, "Travel") then
+ return true
+ end
+ end
+ if cast.travelForm() then
+ ui.debug("Casting Travel Form")
+ return true
+ end
+ end
+
+ -- Auto Shapeshift
+ if not keepAquatic() --and not needsFormBreak()
+ and ((unit.moving() and timeMoving() > ui.value("Shift Wait Time")) or unit.inCombat() or inAggroRange(5))
+ then
+ -- Bear Form
+ if formValue == 3 and (spell.bearForm.known() or spell.direBearForm.known())
+ and ((cast.able.bearForm() and not spell.direBearForm.known()) or cast.able.direBearForm())
+ and not buff.bearForm.exists() and not buff.direBearForm.exists()
+ then
+ if spell.direBearForm.known() then
+ if cast.direBearForm() then
+ ui.debug("Casting Dire Bear Form")
+ return true
+ end
+ else
+ if cast.bearForm() then
+ ui.debug("Casting Bear Form")
+ return true
+ end
+ end
+ end
+ -- Cat Form
+ if (formValue == 2 or (formValue == 3 and unit.level() < 8)) and unit.level() >= 5
+ -- and not unit.inCombat()
+ and cast.able.catForm() and not buff.catForm.exists()
+ and not (buff.aquaticForm.exists() or buff.travelForm.exists() or buff.flightForm.exists() or buff.swiftFlightForm.exists())
+ then
+ if cast.catForm() then
+ ui.debug("Casting Cat Form")
+ return true
+ end
+ end
+ -- Caster Form
+ if formValue == 1 and (buff.bearForm.exists() or buff.direBearForm.exists() or buff.catForm.exists()) then
+ if unit and unit.cancelForm then
+ unit.cancelForm()
+ else
+ br._G.RunMacroText("/CancelForm")
+ end
+ ui.debug("Casting Caster Form")
+ return true
+ end
+ end
+end -- End Action List - Form Management
+
+-- Action List - Bear Form
+actionList.BearForm = function()
+ -- -- Bear Form
+ -- if ((cast.able.bearForm() and not spell.direBearForm.known()) or cast.able.direBearForm())
+ -- and not buff.bearForm.exists() and not buff.direBearForm.exists()
+ -- and not (buff.markOfTheWild.refresh() and ui.checked("Mark of the Wild"))
+ -- and not (buff.thorns.refresh() and ui.checked("Thorns"))
+ -- then
+ -- if spell.direBearForm.known() then
+ -- if cast.direBearForm() then
+ -- ui.debug("Casting Dire Bear Form")
+ -- return true
+ -- end
+ -- else
+ -- if cast.bearForm() then
+ -- ui.debug("Casting Bear Form")
+ -- return true
+ -- end
+ -- end
+ -- end
+ -- Enrage
+ if cast.able.enrage() and not unit.deadOrGhost("target") and not buff.enrage.exists() and rage() < 10 and unit.hp() > 80 then
+ if cast.enrage() then
+ ui.debug("Casting Enrage")
+ return true
+ end
+ end
+ -- Demoralizing Roar
+ if cast.able.demoralizingRoar() and not debuff.demoralizingRoar.exists("target")
+ and #enemies.yards10 > 0
+ then
+ if cast.demoralizingRoar() then
+ ui.debug("Casting Demoralizing Roar")
+ return true
+ end
+ end
+ -- Swipe (multi-target)
+ if cast.able.swipe() and ui.useAOE(8,3) then
+ if cast.swipe() then
+ ui.debug("Casting Swipe")
+ return true
+ end
+ end
+ -- Maul
+ if cast.able.maul("target") and (ui.useST(8,3) or not spell.swipe.known()) then
+ if cast.maul("target") then
+ ui.debug("Casting Maul")
+ return true
+ end
+ end
+ -- Mangle
+ if cast.able.mangleBear("target") then--and not debuff.mangleBear.exists("target") then
+ if cast.mangleBear("target") then
+ ui.debug("Casting Mangle")
+ return true
+ end
+ end
+end -- End Action List - Bear Form
+
+-- Action List - Cat Opener (Stealth + Non-Stealth)
+actionList.CatOpener = function()
+ if not buff.catForm.exists() then return false end
+ if not unit.valid("target") or unit.deadOrGhost("target") or unit.distance("target") >= 5 then return false end
+ if unit.player("target") then return false end
+
+ -- Tiger's Fury gating: if enabled+known and we're at full energy, cast it before opening.
+ if ui.checked("Tiger's Fury") and spell.tigersFury.known() and energy()== 100 and not buff.tigersFury.exists() then
+ if cast.able.tigersFury() and cast.tigersFury() then
+ ui.debug("Casting Tiger's Fury [Opener]")
+ return true
+ end
+ return false
+ end
+
+ local isStealth = buff.prowl.exists()
+ local behindTarget = isBehind("target", "player")
+ local mangleDebuffUp = debuff.mangleCat.exists("target") or debuff.mangleBear.exists("target")
+
+ -- Stealth Opener
+ if isStealth then
+ if behindTarget then
+ -- Behind: fire the best available ability immediately; no need to hold
+ -- Ravage: best stealth ability, requires being behind
+ if cast.able.ravage("target") then
+ if cast.ravage("target") then
+ ui.debug("Casting Ravage [Stealth Opener]")
+ return true
+ end
+ end
+ -- Mangle: apply debuff to improve Shred damage; skip if Ravage known or debuff already up
+ if cast.able.mangleCat("target") and not spell.ravage.known() and not mangleDebuffUp and unit.standingTime() > 1 then
+ if cast.mangleCat("target") then
+ ui.debug("Casting Mangle [Stealth Opener]")
+ return true
+ end
+ end
+ -- Shred: Ravage not known, and debuff is already up or Mangle not known
+ if cast.able.shred("target") and not spell.ravage.known()
+ and (mangleDebuffUp or not spell.mangleCat.known())
+ then
+ if cast.shred("target") then
+ ui.debug("Casting Shred [Stealth Opener]")
+ return true
+ end
+ end
+ else
+ -- Facing the target. Only hold Mangle back for Ravage when BOTH:
+ -- 1) currently prowling (already guaranteed by this block), AND
+ -- 2) Ravage is known
+ -- If either condition fails, use Mangle immediately.
+ if isStealth and spell.ravage.known() and unit.standingTime() <= 1 then return false end
+ -- Positioning window closed, or Ravage not known; commit from the front
+ -- Only cast Mangle if the debuff isn't already present
+ if cast.able.mangleCat("target") and not mangleDebuffUp then
+ if cast.mangleCat("target") then
+ ui.debug("Casting Mangle [Stealth Opener]")
+ return true
+ end
+ end
+ end
+ -- Claw: ultimate fallback — only if Mangle and Shred are not known
+ if cast.able.claw("target") and not spell.mangleCat.known() and not spell.shred.known() then
+ if cast.claw("target") then
+ ui.debug("Casting Claw [Stealth Opener]")
+ return true
+ end
+ end
+ return false
+ end
+
+ -- Non-Stealth Opener
+ -- Mangle first: apply debuff to improve Shred damage; skip if debuff already up
+ if cast.able.mangleCat("target") and not mangleDebuffUp then
+ if cast.mangleCat("target") then
+ ui.debug("Casting Mangle [Opener]")
+ return true
+ end
+ end
+ -- Shred from behind: debuff is up or Mangle not known
+ if behindTarget and cast.able.shred("target") and (mangleDebuffUp or not spell.mangleCat.known()) then
+ if cast.shred("target") then
+ ui.debug("Casting Shred [Opener]")
+ return true
+ end
+ end
+ -- Claw: fallback — only if Mangle and Shred are not known
+ if cast.able.claw("target") and not spell.mangleCat.known() and not spell.shred.known() then
+ if cast.claw("target") then
+ ui.debug("Casting Claw [Opener]")
+ return true
+ end
+ end
+
+ return false
+end
+
+-- Action List - Cat Form
+actionList.CatForm = function()
+ if unit.inCombat("player") and buff.catForm.exists() then
+ -- Cower (threat reduction)
+ -- Only drop threat if a Warrior or Paladin is alive and nearby — in TBC these are the
+ -- only reliable tanks. If none is present (dead or out of range) it is safer to keep
+ -- aggro on ourselves rather than risk handing it to a healer or caster.
+ if ui.checked("Cower") and cast.able.cower() and energy() >= 20 and unit.inGroup()
+ and unit.threatStatus() >= 2 and safeToCower()
+ then
+ if cast.cower() then
+ ui.debug("Casting Cower [Threat Reduction]")
+ return true
+ end
+ end
+ -- Ferocious Bite - Finish Him!
+ local finish = ferociousBiteFinish("target")
+ if cast.able.ferociousBite("target") and energy() >= 35 and finish then
+ if cast.ferociousBite("target") then
+ ui.debug("Casting Ferocious Bite [Finish Him!]")
+ return true
+ end
+ end
+
+ -- Tiger's Fury (opener only - first 6s of combat so it doesn't interrupt the mid-fight rotation)
+ if ui.checked("Tiger's Fury") and cast.able.tigersFury() and buff.catForm.exists()
+ and not buff.tigersFury.exists() and unit.combatTime() < 6
+ then
+ if cast.tigersFury() then
+ ui.debug("Casting Tiger's Fury")
+ return true
+ end
+ end
+
+ -- Faerie Fire (Feral)
+ if ui.checked("Faerie Fire") and cast.able.faerieFireFeral("target") and unit.valid("target")
+ and not buff.prowl.exists()
+ and not debuff.faerieFireFeral.exists("target")
+ and not unit.isElemental("target")
+ and unit.ttd("target") > cd.global.remain()
+ then
+ if cast.faerieFireFeral("target") then
+ ui.debug("Casting Faerie Fire (Feral)")
+ return true
+ end
+ end
+
+ -- 5 Combo Points - Finishers
+ if comboPoints() >= 4 and not finish then
+ -- Rip
+ if ui.checked("Rip") and cast.able.rip("target") and unit.ttd("target") > 6 and not debuff.rip.exists("target") then
+ if cast.rip("target") then
+ ui.debug("Casting Rip")
+ return true
+ end
+ end
+ -- Ferocious Bite (Rip running or not worth applying)
+ if cast.able.ferociousBite("target") and energy() >= 35
+ and (not spell.rip.known() or debuff.rip.exists("target") or unit.ttd("target") <= 6)
+ then
+ if cast.ferociousBite("target") then
+ ui.debug("Casting Ferocious Bite")
+ return true
+ end
+ end
+ end
+
+ -- Combo Point Builders
+ if (comboPoints() < 5 or energy() >= 60 or buff.clearcasting.exists()) and not finish then
+ -- Ravage (from Prowl)
+ local behindRavage = isBehind("target", "player")
+ if cast.able.ravage("target") and buff.prowl.exists() and behindRavage then
+ if cast.ravage("target") then
+ ui.debug("Casting Ravage")
+ return true
+ end
+ end
+ -- Rake
+ if ui.checked("Rake") and cast.able.rake("target") and debuff.rake.refresh("target")
+ and (unit.ttd("target") > 9 or not spell.ferociousBite.known())
+ and not buff.clearcasting.exists()
+ then
+ if cast.rake("target") then
+ ui.debug("Casting Rake")
+ return true
+ end
+ end
+ -- Mangle
+ local behindDyn5 = isBehind("target", "player")
+ if cast.able.mangleCat("target") and (not behindDyn5 or not spell.shred.known()
+ or (debuff.mangleCat.refresh("target","any") and not debuff.mangleBear.exists("target","any"))
+ ) then
+ if cast.mangleCat("target") then
+ ui.debug("Casting Mangle")
+ return true
+ end
+ end
+ -- Shred (from behind)
+ local triedShred = false
+ if behindDyn5 and cast.able.shred("target")
+ and (debuff.mangleCat.exists("target") or debuff.mangleBear.exists("target") or not spell.mangleCat.known())
+ then
+ if not unit.facing("target", "player") then
+ triedShred = true
+ if cast.shred("target") then
+ ui.debug("Casting Shred")
+ return true
+ end
+ end
+ end
+ -- Claw (not behind)
+ if cast.able.claw("target") and (not spell.shred.known() or not behindDyn5) and not spell.mangleCat.known() then
+ if cast.claw("target") then
+ ui.debug("Casting Claw")
+ return true
+ end
+ end
+ end
+ -- Powershift for Energy (Cat Form with Furor talent)
+ -- Runs last so finishers and builders are always preferred over shifting.
+ if ui.checked("Powershifting") and cast.able.catForm() and unit.inCombat()
+ and not buff.clearcasting.exists() and mana() > formCost
+ then
+ local immediateShiftThreshold = math.max(10, ui.round(ui.value("Energy")*(mana.percent()/100), 0))
+ if energy() < immediateShiftThreshold then
+ if cast.macro("/cast !"..spell.catForm.name()) then
+ ui.debug("Powershift for Energy")
+ powershiftReady = false
+ return true
+ end
+ end
+ end
+ end
+end -- End Action List - Cat Form
+
+-- Action List - Caster Form
+actionList.CasterForm = function()
+ -- Moonfire
+ if cast.able.moonfire(units.dyn40AOE) and debuff.moonfire.refresh(units.dyn40AOE) then
+ if cast.moonfire(units.dyn40AOE) then
+ ui.debug("Casting Moonfire")
+ return true
+ end
+ end
+ -- Starfire (if available)
+ if cast.able.starfire() and not unit.moving() and cast.able.starfire() and unit.level() >= 20
+ and (debuff.moonfire.exists(units.dyn40AOE) or not spell.moonfire.known())
+ and (not cast.last.starfire() or cast.timeSinceLast.starfire() > unit.gcd(true) + 0.5)
+ then
+ if cast.starfire() then
+ ui.debug("Casting Starfire")
+ return true
+ end
+ end
+ -- Wrath
+ if cast.able.wrath() and not unit.moving()
+ and (unit.level() < 20 or not spell.starfire.known())
+ and (debuff.moonfire.exists(units.dyn40AOE) or not spell.moonfire.known())
+ and (not cast.last.wrath() or cast.timeSinceLast.wrath() > unit.gcd(true) + 0.5)
+ then
+ if cast.wrath() then
+ ui.debug("Casting Wrath")
+ return true
+ end
+ end
+end -- End Action List - Caster Form
+
+-- Action List - Pre-Combat
+actionList.PreCombat = function()
+ if not unit.inCombat() and not (unit.flying() or unit.mounted()) then
+ if not (buff.prowl.exists() or buff.shadowmeld.exists()) then
+ -- Prowl
+ if cast.able.prowl("player") and buff.catForm.exists() --[[and autoProwl()]] and ui.mode.prowl == 1
+ and inAggroRange() and not buff.prowl.exists() and not unit.resting() and not unit.deadOrGhost("target")
+ then
+ if cast.prowl("player") then
+ ui.debug("Casting Prowl [Precombat]")
+ return true
+ end
+ end
+ end -- End No Stealth
+ if unit.valid("target") then
+ local thisDistance = unit.distance("target") or 99
+ -- Wrath
+ if formValue == 1 and not unit.moving()
+ and not (buff.catForm.exists() or buff.bearForm.exists() or buff.direBearForm.exists()) and thisDistance < 30
+ then
+ if cast.able.wrath("target") and (unit.level() < 2 or not cast.last.wrath() or cast.timeSinceLast.wrath() > unit.gcd(true) + 0.5) then
+ if cast.wrath("target") then
+ ui.debug("Casting Wrath [Precombat]")
+ return true
+ end
+ end
+ end
+ if thisDistance < 5 then
+ -- Cat Opener (Stealth + Non-Stealth)
+ if formValue == 2 then
+ if actionList.CatOpener() then return true end
+ end
+ -- Bear Form
+ if formValue == 3 then
+ -- Bear Form
+ if ((cast.able.bearForm() and not spell.direBearForm.known()) or cast.able.direBearForm())
+ and not buff.bearForm.exists() and not buff.direBearForm.exists()
+ and not (buff.markOfTheWild.refresh() and ui.checked("Mark of the Wild"))
+ and not (buff.thorns.refresh() and ui.checked("Thorns"))
+ then
+ if spell.direBearForm.known() then
+ if cast.direBearForm() then
+ ui.debug("Casting Dire Bear Form")
+ return true
+ end
+ else
+ if cast.bearForm() then
+ ui.debug("Casting Bear Form")
+ return true
+ end
+ end
+ end
+ if (buff.bearForm.exists() or buff.direBearForm.exists()) then
+ -- Enrage
+ if cast.able.enrage() and not unit.deadOrGhost("target") and not buff.enrage.exists() and rage() < 10 then
+ if cast.enrage() then
+ ui.debug("Casting Enrage [Precombat]")
+ return true
+ end
+ end
+ -- Swipe
+ if cast.able.swipe() and ui.useAOE(8,3) then
+ if cast.swipe() then
+ ui.debug("Casting Swipe [Precombat]")
+ return true
+ end
+ end
+ -- Maul
+ if cast.able.maul("target") and (ui.useST(8,3) or not spell.swipe.known()) then
+ if cast.maul("target") then
+ ui.debug("Casting Maul [Precombat]")
+ return true
+ end
+ end
+ end
+ end
+ -- Auto Attack (do not break Prowl)
+ if not buff.prowl.exists() and not cast.auto.autoAttack() and unit.valid("target") and not unit.deadOrGhost("target") and unit.distance("target") < 5 then
+ br._G.StartAttack()
+ ui.debug("Casting Auto Attack [Precombat]")
+ return true
+ end
+ end
+ end
+ end
+end -- End Action List - PreCombat
+
+-- Action List - Combat
+actionList.Combat = function()
+ if unit.inCombat() and unit.valid("target") then
+ ------------------------
+ --- In Combat - Main ---
+ ------------------------
+ -- Start Attack (do not break Prowl)
+ if not buff.prowl.exists() and unit.exists("target") and not cast.auto.autoAttack() and not unit.deadOrGhost("target") then
+ br._G.StartAttack()
+ ui.debug("Casting Auto Attack")
+ return true
+ end
+
+ -- Call Action List - Cooldowns
+ if actionList.Cooldowns() then return true end
+
+ -- Wait for swing if in melee range
+ -- if unit.exists("target") and unit.distance("target") < 5 and
+ -- (formValue == 2 and buff.catForm.exists()
+ -- or formValue == 3 and (buff.bearForm.exists() or buff.direBearForm.exists()))
+ -- then
+ -- local swing = br.swingTimer
+ -- -- ui.debug("Swing Timer: " .. tostring(swing) .. " | Distance: " .. tostring(unit.distance("target")))
+ -- local waitThreshold = 0.15 -- seconds; tune between 0.15-0.30
+ -- if swing and swing > 0 and swing < waitThreshold then
+ -- if not br.waitingForSwing then
+ -- br.waitingForSwing = true
+ -- br.waitingForSwingTimeout = br._G.GetTime() + 1.5 -- safety timeout
+ -- ui.debug("Waiting for swing: " .. br.functions.misc:round2(swing,2) .. "s")
+ -- end
+ -- -- clear waiting flag if swing occurred or timeout expired
+ -- if br.swingTimer == 0 or (br.waitingForSwingTimeout and br._G.GetTime() >= br.waitingForSwingTimeout) then
+ -- br.waitingForSwing = false
+ -- br.waitingForSwingTimeout = nil
+ -- else
+ -- return true
+ -- end
+ -- end
+ -- end
+
+ -- Call Action List - Bear Form
+ if formValue == 3 and (buff.bearForm.exists() or buff.direBearForm.exists()) and unit.exists("target") and unit.distance("target") < 5 then
+ if actionList.BearForm() then return true end
+ end
+
+ -- Call Action List - Cat Form
+ if formValue == 2 and unit.exists("target") and unit.distance("target") < 5 then
+ if buff.prowl.exists() then
+ -- While prowled, only CatOpener runs; CatForm is never called.
+ -- If CatOpener returns false (still repositioning), we yield and
+ -- try again next tick rather than falling through to CatForm's Mangle.
+ if actionList.CatOpener() then return true end
+ else
+ if actionList.CatForm() then return true end
+ end
+ end
+
+ -- Call Action List - Caster Form
+ if formValue == 1 and not (buff.catForm.exists() or buff.bearForm.exists() or buff.direBearForm.exists()) then
+ if actionList.CasterForm() then return true end
+ end
+ end -- End In Combat Rotation
+end -- End Action list - Combat
+
+----------------
+--- ROTATION ---
+----------------
+local function runRotation()
+ ---------------------
+ --- Define Locals ---
+ ---------------------
+ -- BR API Locals
+ buff = br.player.buff
+ cast = br.player.cast
+ cd = br.player.cd
+ comboPoints = br.player.power.comboPoints
+ debuff = br.player.debuff
+ enemies = br.player.enemies
+ energy = br.player.power.energy
+ mana = br.player.power.mana
+ rage = br.player.power.rage
+ module = br.player.module
+ ui = br.player.ui
+ unit = br.player.unit
+ units = br.player.units
+ spell = br.player.spell
+ -- General Locals
+ profileStop = profileStop or false
+ haltProfile = (unit.inCombat() and profileStop) or ui.pause() or ui.mode.rotation == 4 or unit.id("target") == 156716
+ formValue = ui.mode.forms
+ formCost = 0
+ if not br.player.localTrinkets then br.player.localTrinkets = true end
+ if ui.mode.forms == 2 then formCost = cast.cost.catForm() end
+ if ui.mode.forms == 3 then formCost = spell.direBearForm.known() and cast.cost.direBearForm() or cast.cost.bearForm() end
+ -- Units
+ units.get(5) -- Makes a variable called, "target"
+ -- units.get(40) -- Makes a variable called, units.dyn40
+ units.get(40, true) -- Makes a variable called, units.dyn40AOE
+ -- Enemies
+ enemies.get(5, "player", false, true) -- Makes a variable called, enemies.yards5f
+ enemies.get(20) -- Makes a varaible called, enemies.yards20
+ enemies.get(20, "player", true) -- makes enemies.yards20nc
+ enemies.get(40) -- Makes a varaible called, enemies.yards40
+
+ -- Profile Specific Locals
+ -- fbMaxEnergy = energy() >= 50
+
+ -- Update Last Form tracking
+ -- updateLastForm()
+
+ -- -- Omen of Clarity Logic
+ -- local omenSetting = ui.value("Omen of Clarity")
+ -- needsHealing = (ui.checked("Regrowth") and unit.hp() <= ui.value("Regrowth"))
+ -- or (ui.checked("Healing Touch") and unit.hp() <= ui.value("Healing Touch"))
+ -- or (ui.checked("Rejuvenation") and unit.hp() <= ui.value("Rejuvenation"))
+
+ -- freeDPS = omenSetting ~= 4 and omenSetting ~= 2 and buff.clearcasting.exists()
+ -- and (not needsHealing or omenSetting == 3)
+
+ -- freeHeal = omenSetting ~= 4 and omenSetting ~= 3 and buff.clearcasting.exists()
+
+ -- No Shapeshift Power check
+ -- noShapeshiftPower = ((not buff.catForm.exists() or (buff.catForm.exists() and energy() < ui.value("Energy")))
+ -- and (not (buff.bearForm.exists() and buff.direBearForm.exists())
+ -- or ((buff.bearForm.exists() or buff.direBearForm.exists()) and rage() < ui.value("Rage"))))
+ -- or not unit.inCombat()
+
+ if not unit.inCombat() and not unit.exists("target") then
+ if profileStop then profileStop = false end
+ end
+
+ -- ui.chatOverlay("Shift At: "..tostring(ui.round(math.max(10, ui.value("Energy")*(mana.percent()/100)),0)))
+
+ ---------------------
+ --- Begin Profile ---
+ ---------------------
+ -- Profile Stop | Pause
+ if not unit.inCombat() and not unit.exists("target") and profileStop then
+ profileStop = false
+ elseif haltProfile then
+ return true
+ else
+ ---------------------------------
+ --- Out Of Combat - Rotations ---
+ ---------------------------------
+ -----------------------
+ --- Extras Rotation ---
+ -----------------------
+ if actionList.Extra() then return true end
+ -----------------
+ --- Defensive ---
+ -----------------
+ if actionList.Defensive() then return true end
+ -----------------------
+ --- Form Management ---
+ -----------------------
+ if actionList.FormManagement() then return true end
+ ------------------
+ --- Pre-Combat ---
+ ------------------
+ if actionList.PreCombat() then return true end
+ -----------------------------
+ --- In Combat - Rotations ---
+ -----------------------------
+ if actionList.Combat() then return true end
+ end -- Pause
+ return true
+end -- End runRotation
+local id = 283 -- Change to the spec id profile is for.
+local expansion = br.isBC -- Change to the expansion the profile is for.
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
+ name = rotationName,
+ toggles = createToggles,
+ options = createOptions,
+ run = runRotation,
+})
diff --git a/Rotations/Druid/Druid/Druid-Vanilla.lua b/Rotations/Druid/Druid/Druid-Vanilla.lua
new file mode 100644
index 0000000000..2ca1b48c4b
--- /dev/null
+++ b/Rotations/Druid/Druid/Druid-Vanilla.lua
@@ -0,0 +1,1456 @@
+local rotationName = "CuteOne" -- Change to name of profile listed in options drop down
+
+---------------
+--- Toggles ---
+---------------
+local function createToggles() -- Define custom toggles
+ -- Rotation Button
+ local RotationModes = {
+ [1] = { mode = "Auto", value = 1, overlay = "Automatic Rotation", tip = "Swaps between Single and Multiple based on number of #enemies.yards8 in range.", highlight = 1, icon = br.player.spells.swipe },
+ [2] = { mode = "Multi", value = 2, overlay = "Multiple Target Rotation", tip = "Forces Multiple Target Rotation.", highlight = 0, icon = br.player.spells.swipe },
+ [3] = { mode = "Single", value = 3, overlay = "Single Target Rotation", tip = "Forces Single Target Rotation.", highlight = 0, icon = br.player.spells.shred },
+ [4] = { mode = "Off", value = 4, overlay = "DPS Rotation Disabled", tip = "Disable DPS Rotation", highlight = 0, icon = br.player.spells.healingTouch }
+ };
+ br.ui:createToggle(RotationModes, "Rotation", 1, 0)
+ -- Defensive Button
+ local DefensiveModes = {
+ [1] = { mode = "On", value = 1, overlay = "Defensive Enabled", tip = "Includes Defensive Cooldowns.", highlight = 1, icon = br.player.spells.healingTouch },
+ [2] = { mode = "Off", value = 2, overlay = "Defensive Disabled", tip = "No Defensives will be used.", highlight = 0, icon = br.player.spells.healingTouch }
+ };
+ br.ui:createToggle(DefensiveModes, "Defensive", 2, 0)
+ -- Interrupt Button
+ local InterruptModes = {
+ [1] = { mode = "On", value = 1, overlay = "Interrupts Enabled", tip = "Includes Basic Interrupts.", highlight = 1, icon = br.player.spells.bash },
+ [2] = { mode = "Off", value = 2, overlay = "Interrupts Disabled", tip = "No Interrupts will be used.", highlight = 0, icon = br.player.spells.bash }
+ };
+ br.ui:createToggle(InterruptModes, "Interrupt", 3, 0)
+ -- Form Button
+ local FormModes = {
+ [1] = { mode = "Caster", value = 1, overlay = "Caster Form", tip = "Will force and use Caster Form", highlight = 1, icon = br.player.spells.moonkinForm },
+ [2] = { mode = "Cat", value = 2, overlay = "Cat Form", tip = "Will force and use Cat Form", highlight = 0, icon = br.player.spells.catForm },
+ [3] = { mode = "Bear", value = 3, overlay = "Bear Form", tip = "Will force and use Bear Form", highlight = 0, icon = br.player.spells.bearForm }
+ };
+ br.ui:createToggle(FormModes, "Forms", 4, 0)
+ -- Prowl Button
+ local ProwlModes = {
+ [1] = { mode = "On", value = 1, overlay = "Prowl Enabled", tip = "Rotation will use Prowl", highlight = 1, icon = br.player.spells.prowl },
+ [2] = { mode = "Off", value = 2, overlay = "Prowl Disabled", tip = "Rotation will not use Prowl", highlight = 0, icon = br.player.spells.prowl }
+ };
+ br.ui:createToggle(ProwlModes, "Prowl", 5, 0)
+end
+
+---------------
+--- OPTIONS ---
+---------------
+local function createOptions()
+ local optionTable
+
+ local function rotationOptions()
+ local section
+ -----------------------
+ --- GENERAL OPTIONS ---
+ -----------------------
+ section = br.ui:createSection(br.ui.window.profile, "General")
+ br.ui:createSpinnerWithout(section, "Shift Wait Time", 2, 0, 5, 1,
+ "|cffFFFFFFTime in seconds the profile will wait while moving to shift. Combat is instant!")
+ br.ui:createCheckbox(section, "Behind Debug", "|cffFFFFFFDebug behind/facing checks for Shred/Ravage.")
+ -- Powershifting
+ br.ui:createCheckbox(section, "Powershifting", "|cffFFFFFFFeral DPS In-Form Shifting for Energy")
+ -- Prowl
+ br.ui:createCheckbox(section, "Prowl", "|cffFFFFFFUses Prowl when near aggro range")
+ -- Cat Opener
+ br.ui:createDropdown(section, "Cat Opener",
+ { "|cffFFFFFFPounce", "|cffFFFFFFRavage", "|cffFFFFFFShred", "|cffFFFFFFRake" }, 2,
+ "|cffFFFFFFSelect Spell to Open with from Stealth")
+ br.ui:checkSectionState(section)
+ --------------------------
+ --- FORM MANAGEMENT ---
+ --------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Form Management")
+ -- Aquatic Form
+ br.ui:createCheckbox(section, "Aquatic Form", "|cffFFFFFFEnable/Disable Using Aquatic Form")
+ -- Travel Form
+ br.ui:createCheckbox(section, "Travel Form", "|cffFFFFFFEnable/Disable Using Travel Form")
+ -- Last Form
+ br.ui:createCheckbox(section, "Last Form", "|cffFFFFFFEnable/Disable Returning to Last Form")
+ -- Energy Threshold
+ br.ui:createSpinner(section, "Energy", 30, 1, 100, 1, "|cffFFFFFFCat Form Energy Threshold to Break Form")
+ -- Rage Threshold
+ br.ui:createSpinner(section, "Rage", 10, 1, 100, 1, "|cffFFFFFFBear Form Rage Threshold to Break Form")
+ br.ui:checkSectionState(section)
+ ----------------------
+ --- BLEED OPTIONS ---
+ ----------------------
+ section = br.ui:createSection(br.ui.window.profile, "Bleeds")
+ -- Rake
+ br.ui:createCheckbox(section, "Rake", "|cffFFFFFFEnable/Disable Using Rake")
+ -- Rip
+ br.ui:createCheckbox(section, "Rip", "|cffFFFFFFEnable/Disable Using Rip")
+ br.ui:checkSectionState(section)
+ ---------------------
+ --- BUFF OPTIONS ---
+ ---------------------
+ section = br.ui:createSection(br.ui.window.profile, "Buffs")
+ -- Self-Innervate
+ br.ui:createCheckbox(section, "Self-Innervate", "|cffFFFFFFEnable/Disable Using Innervate on Self")
+ -- Mark of the Wild
+ br.ui:createDropdown(section, "Mark of the Wild",
+ { "|cffFFFFFFPlayer", "|cffFFFFFFTarget", "|cffFFFFFFMouseover", "|cffFFFFFFFocus", "|cffFFFFFFGroup" }, 1,
+ "|cffFFFFFFSet how to use Mark of the Wild")
+ -- Omen of Clarity
+ br.ui:createDropdown(section, "Omen of Clarity",
+ { "|cffFFFFFFAny", "|cffFFFFFFFocus DPS", "|cffFFFFFFFocus Healing", "|cffFFFFFFNone" }, 1,
+ "|cffFFFFFFSelect how to use Omen of Clarity procs")
+ -- Thorns
+ br.ui:createDropdown(section, "Thorns",
+ { "|cffFFFFFFPlayer", "|cffFFFFFFTarget", "|cffFFFFFFMouseover", "|cffFFFFFFFocus", "|cffFFFFFFGroup" }, 1,
+ "|cffFFFFFFSet how to use Thorns")
+ -- Tiger's Fury
+ br.ui:createCheckbox(section, "Tiger's Fury", "|cffFFFFFFEnable/Disable Using Tiger's Fury")
+ br.ui:checkSectionState(section)
+ -------------------------
+ --- DEFENSIVE OPTIONS ---
+ -------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Defensive")
+ -- Self-Heal In Group
+ br.ui:createCheckbox(section, "Self Heal In Group", "|cffFFFFFFEnable/Disable Healing Self while Grouped")
+ -- Healing Touch
+ br.ui:createSpinner(section, "Healing Touch", 40, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ -- Regrowth
+ br.ui:createSpinner(section, "Regrowth", 60, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ -- Rejuvenation
+ br.ui:createSpinner(section, "Rejuvenation", 80, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ -- Barkskin
+ br.ui:createSpinner(section, "Barkskin", 50, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ -- Abolish Poison
+ br.ui:createCheckbox(section, "Abolish Poison", "|cffFFFFFFEnable/Disable Using Abolish Poison")
+ -- Cure Poison
+ br.ui:createCheckbox(section, "Cure Poison", "|cffFFFFFFEnable/Disable Using Cure Poison")
+ -- Remove Curse
+ br.ui:createCheckbox(section, "Remove Curse", "|cffFFFFFFEnable/Disable Using Remove Curse")
+ -- Entangling Roots
+ br.ui:createCheckbox(section, "Entangling Roots", "|cffFFFFFFEnable/Disable Using Entangling Roots on fleeing enemies")
+ -- Faerie Fire
+ br.ui:createCheckbox(section, "Faerie Fire", "|cffFFFFFFEnable/Disable Using Faerie Fire/Faerie Fire Feral")
+ -- Nature's Grasp
+ br.ui:createCheckbox(section, "Nature's Grasp", "|cffFFFFFFEnable/Disable Using Nature's Grasp")
+ -- -- Health Potion
+ -- br.ui:createCheckbox(section, "Health Potion", "|cffFFFFFFEnable/Disable Using Health Potion")
+ -- -- Mana Potion
+ -- br.ui:createCheckbox(section, "Mana Potion", "|cffFFFFFFEnable/Disable Using Mana Potion")
+ -- Bash
+ br.ui:createSpinner(section, "Bash", 50, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ br.ui:checkSectionState(section)
+ -------------------------
+ --- Interrupt Options ---
+ -------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Interrupts")
+ -- Bash
+ br.ui:createCheckbox(section, "Bash - Int")
+ -- Interrupt Percentage
+ br.ui:createSpinnerWithout(section, "Interrupt At", 0, 0, 95, 5,
+ "|cffFFFFFFCast Percent to Cast At (0 is random)")
+ br.ui:checkSectionState(section)
+ ----------------------
+ --- TOGGLE OPTIONS ---
+ ----------------------
+ section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
+ -- Single/Multi Toggle
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
+ -- Defensive Key Toggle
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
+ -- Interrupt Key Toggle
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
+ -- Forms Key Toggle
+ br.ui:createDropdownWithout(section, "Forms Mode", br.ui.dropOptions.Toggle, 6)
+ -- Prowl Key Toggle
+ br.ui:createDropdownWithout(section, "Prowl Mode", br.ui.dropOptions.Toggle, 6)
+ br.ui:checkSectionState(section)
+ end
+ optionTable = { {
+ [1] = "Rotation Options",
+ [2] = rotationOptions,
+ } }
+ return optionTable
+end
+
+--------------
+--- Locals ---
+--------------
+-- BR API Locals - Many of these are located from System/API, this is a sample of commonly used ones but no all inclusive
+local buff
+local cast
+local cd
+local comboPoints
+local debuff
+local enemies
+local energy
+local rage
+local module
+local ui
+local unit
+local units
+local spell
+local var = {}
+-- General Locals - Common Non-BR API Locals used in profiles
+local haltProfile
+local profileStop
+-- Profile Specific Locals - Any custom to profile locals
+local actionList = {}
+local fbMaxEnergy
+local movingTimer
+local lastForm
+local lastFormBuff = false
+local freeDPS
+local freeHeal
+local noShapeshiftPower
+local needsHealing
+
+-----------------
+--- Functions --- -- List all profile specific custom functions here
+-----------------
+-- AutoProwl
+local function autoProwl()
+ if not unit.inCombat() and not buff.prowl.exists() then
+ if #enemies.yards20 > 0 then return true end
+ if #enemies.yards20nc > 0 then
+ for i = 1, #enemies.yards20nc do
+ local thisUnit = enemies.yards20nc[i]
+ local threatRange = math.max((20 + (unit.level(thisUnit) - unit.level())), 5)
+ local react = unit.reaction(thisUnit) or 10
+ if unit.distance(thisUnit) < threatRange and (react < 4 or (unit.isUnit("target", thisUnit) and react == 4)) and unit.enemy(thisUnit) and unit.canAttack(thisUnit) then
+ return true
+ end
+ end
+ end
+ -- if unit.isDummy("target") and unit.distance("target") < 20 then return true end
+ end
+ return false
+end
+-- Ferocious Bite Finish
+local function ferociousBiteFinish(thisUnit)
+ local desc = br._G.C_Spell.GetSpellDescription(spell.ferociousBite.id()) or ""
+ if desc == "" then return false end
+
+ local finishHim = false
+ if thisUnit == nil then thisUnit = units.dyn5 end
+ if comboPoints <= 0 or unit.isDummy(thisUnit) then return false end
+
+ local comboStart = desc:find(" " .. comboPoints .. " ", 1, true)
+ if comboStart == nil then return false end
+
+ local damageList = desc:sub(comboStart + 2)
+ local colonPos = damageList:find(": ", 1, true)
+ if colonPos == nil then return false end
+ damageList = damageList:sub(colonPos + 2)
+
+ local spacePos = damageList:find(" ", 1, true) or (#damageList + 1)
+ local token = damageList:sub(1, spacePos - 1):gsub(",", "")
+
+ local low = token:match("^(%d+)%-%d+$") or token:match("^(%d+)$")
+ local lower = tonumber(low)
+ if lower == nil then return false end
+
+ finishHim = lower >= (unit.health(thisUnit) or 0)
+ return finishHim
+end
+-- Time Moving
+local function timeMoving()
+ if movingTimer == nil then movingTimer = br._G.GetTime() end
+ if not unit.moving() then
+ movingTimer = br._G.GetTime()
+ end
+ return br._G.GetTime() - movingTimer
+end
+
+-- Behind check (nil-safe)
+-- Prefer the engine API so all profiles share the same fix/behavior.
+local function isBehind(thisUnit, otherUnit, degrees)
+ if unit == nil or unit.isBehind == nil then return false end
+ local result = unit.isBehind(thisUnit, otherUnit, degrees)
+ if result == nil or result == false then
+ if ui and ui.checked and ui.checked("Behind Debug") then
+ ui.debug(string.format(
+ "isBehind: FALSE (angle missing or not behind) | thisUnit=%s | otherUnit=%s | degrees=%s",
+ tostring(thisUnit), tostring(otherUnit), tostring(degrees)
+ ))
+ end
+ return false
+ end
+ return true
+end
+
+local function debugBehind(label, thisUnit)
+ if ui == nil or ui.checked == nil or not ui.checked("Behind Debug") then return end
+ if thisUnit == nil or not unit.exists(thisUnit) then return end
+ local targetName = unit.exists("target") and unit.name("target") or "none"
+ local thisName = unit.name(thisUnit) or tostring(thisUnit)
+ local isTarget = unit.exists("target") and unit.isUnit(thisUnit, "target")
+ local dist = unit.distance(thisUnit) or -1
+
+ -- Engine-facing results
+ local facingPlayer = unit.facing(thisUnit, "player")
+ local behindPlayer = unit.isBehind(thisUnit, "player")
+
+ -- Raw unlocker-facing check (front 180 cone)
+ local rawFacing180
+ if br and br._G and br._G.ObjectIsFacing then
+ local ok, res = pcall(br._G.ObjectIsFacing, thisUnit, "player", 180)
+ rawFacing180 = ok and res or nil
+ end
+
+ ui.debug(string.format(
+ "%s | unit=%s (isTarget=%s, dist=%.1f) | target=%s | facingPlayer=%s | behindPlayer=%s | rawFacing180=%s",
+ tostring(label), tostring(thisName), tostring(isTarget), tonumber(dist) or -1, tostring(targetName),
+ tostring(facingPlayer), tostring(behindPlayer), tostring(rawFacing180)
+ ))
+end
+
+-- Update Last Form
+local function updateLastForm()
+ if buff.direBearForm.exists() then lastForm = spell.direBearForm end
+ if buff.bearForm.exists() then lastForm = spell.bearForm end
+ if buff.catForm.exists() then lastForm = spell.catForm end
+ if buff.moonkinForm.exists() then lastForm = spell.moonkinForm end
+
+ lastFormBuff = false
+ if buff.direBearForm.exists() or buff.bearForm.exists() or buff.catForm.exists() or buff.moonkinForm.exists() then
+ lastFormBuff = true
+ end
+
+ -- Set default last form if none set
+ if lastForm == nil and unit.level() >= 10 and spell.bearForm.known() then
+ lastForm = spell.direBearForm.known() and spell.direBearForm or spell.bearForm
+ end
+ if lastForm == nil and unit.level() >= 20 and spell.catForm.known() then
+ lastForm = spell.catForm
+ end
+end
+local function safeCancelForm(outOfFormSpellRef, reason, treatOutSpellAsFree)
+ if unit.exists("target") and unit.friend("target") then
+ -- ui.debug("Skip Cancel Form [" .. tostring(reason) .. "] (friendly target)")
+ return false
+ end
+
+ if unit == nil or unit.canCancelFormFor == nil or unit.formBreakManaRequired == nil then
+ -- Fallback (shouldn't happen during normal profile execution)
+ if buff.catForm.exists() or buff.bearForm.exists() or buff.travelForm.exists() or buff.aquaticForm.exists() then
+ if unit and unit.cancelForm then
+ unit.cancelForm()
+ else
+ br._G.RunMacroText("/CancelForm")
+ end
+ ui.debug("Cancel Form [" .. tostring(reason) .. "]")
+ return true
+ end
+ return false
+ end
+
+ if not unit.canCancelFormFor(outOfFormSpellRef, lastForm, treatOutSpellAsFree) then
+ local requiredMana, currentMana = unit.formBreakManaRequired(outOfFormSpellRef, lastForm, treatOutSpellAsFree)
+ -- ui.debug("Skip Cancel Form [" .. tostring(reason) .. "] (mana " .. tostring(currentMana) .. " < need " .. tostring(requiredMana) .. ")")
+ return false
+ end
+
+ if unit and unit.cancelForm then
+ unit.cancelForm()
+ else
+ br._G.RunMacroText("/CancelForm")
+ end
+ ui.debug("Cancel Form [" .. tostring(reason) .. "]")
+ return true
+end
+
+-- Powershift for Energy
+local function powershift()
+ if cd.global.remain() == 0 and unit.combatTime() > 1 then
+ for i = 1, br._G.GetNumShapeshiftForms() do
+ local _, name, active = br._G.GetShapeshiftFormInfo(i)
+ if name and active then
+ br._G.CancelShapeshiftForm()
+ ui.debug("Powershift - Canceling Form")
+ br._G.CastShapeshiftForm(i)
+ return true
+ end
+ end
+ end
+ return false
+end
+
+-- In Aggro Range Check
+local function inAggroRange(offset)
+ offset = offset or 0
+ for i = 1, #enemies.yards40 do
+ local thisUnit = enemies.yards40[i]
+ local threatRange = math.max((20 + (unit.level(thisUnit) - unit.level())), 5) + offset
+ local react = unit.reaction(thisUnit) or 10
+ if unit.distance(thisUnit) < threatRange and (react < 4 or (unit.isUnit("target", thisUnit) and react == 4)) then
+ return true
+ end
+ end
+ return false
+end
+
+local getBuffUnitOption = function(option)
+ local thisTar = ui.value(option)
+ local thisUnit
+ if thisTar == 1 then
+ thisUnit = "player"
+ end
+ if thisTar == 2 then
+ thisUnit = "target"
+ if not unit.exists(thisUnit) then
+ thisUnit = "player"
+ end
+ end
+ if thisTar == 3 then
+ thisUnit = "mouseover"
+ if not unit.exists(thisUnit) then
+ thisUnit = "player"
+ end
+ end
+ if thisTar == 4 then
+ thisUnit = "focus"
+ if not unit.exists(thisUnit) then
+ thisUnit = "player"
+ end
+ end
+ if thisTar == 5 then
+ thisUnit = "player"
+ if #br.engines.healingEngine.friend > 1 then
+ for i = 1, #br.engines.healingEngine.friend do
+ local nextUnit = br.engines.healingEngine.friend[i].unit
+ local needsBuff = false
+ if option == "Mark of the Wild" then
+ needsBuff = buff.markOfTheWild.refresh(nextUnit)
+ elseif option == "Thorns" then
+ needsBuff = buff.thorns.refresh(nextUnit)
+ end
+ if needsBuff and unit.distance(nextUnit) < 40 then
+ thisUnit = nextUnit
+ break
+ end
+ end
+ end
+ end
+ return thisUnit
+end
+
+-- Check if we need to cast anything that requires breaking form
+local function needsFormBreak()
+ -- Buffs
+ if ui.checked("Mark of the Wild") then
+ var.markUnit = getBuffUnitOption("Mark of the Wild")
+ if cast.able.markOfTheWild(var.markUnit) and buff.markOfTheWild.refresh(var.markUnit)
+ and not unit.inCombat() and not unit.resting() and unit.distance(var.markUnit) < 40
+ and (var.markUnit == "player" or unit.player(var.markUnit))
+ then
+ return true
+ end
+ end
+ if ui.checked("Thorns") then
+ var.thornUnit = getBuffUnitOption("Thorns")
+ if cast.able.thorns(var.thornUnit) and buff.thorns.refresh(var.thornUnit)
+ and not unit.inCombat() and not unit.resting() and unit.distance(var.thornUnit) < 40
+ and (var.thornUnit == "player" or unit.player(var.thornUnit))
+ then
+ return true
+ end
+ end
+ if ui.value("Omen of Clarity") ~= 4 and not unit.inCombat() then
+ if cast.able.omenOfClarity("player") and buff.omenOfClarity.refresh("player") then
+ return true
+ end
+ end
+
+ -- Defensive actions
+ if ui.useDefensive() then
+ -- Dispels
+ if ui.checked("Abolish Poison") and cast.able.abolishPoison("player")
+ and not buff.abolishPoison.exists("player") and unit.hasDispel("player", spell.abolishPoison)
+ then
+ return true
+ end
+ if ui.checked("Cure Poison") and cast.able.curePoison("player")
+ and not ui.checked("Abolish Poison") and unit.hasDispel("player", spell.curePoison)
+ then
+ return true
+ end
+ if ui.checked("Remove Curse") and cast.able.removeCurse("player")
+ and not unit.inCombat() and unit.hasDispel("player", spell.removeCurse)
+ then
+ return true
+ end
+ -- Entangling Roots
+ if ui.checked("Entangling Roots") and cast.able.entanglingRoots("target")
+ and unit.exists("target") and not unit.facing("target", "player") and unit.moving("target")
+ and unit.valid("target") and unit.hp("target") < 100 and not debuff.entanglingRoots.exists("target")
+ and unit.distance("target") > 8 and unit.inCombat()
+ then
+ return true
+ end
+ -- Nature's Grasp
+ if ui.checked("Nature's Grasp") and cast.able.naturesGrasp()
+ and not buff.naturesGrasp.exists("player") and unit.inCombat()
+ and #enemies.yards10 > 0
+ then
+ return true
+ end
+ -- Healing
+ if ui.checked("Regrowth") and cast.able.regrowth("player") and not cast.current.regrowth() and not unit.moving()
+ and unit.hp() <= ui.value("Regrowth") and not buff.regrowth.exists("player")
+ and (freeHeal or noShapeshiftPower)
+ and (not unit.inGroup() or ui.checked("Self Heal In Group"))
+ then
+ return true
+ end
+ if ui.checked("Rejuvenation") and not cast.current.rejuvenation("target") then
+ if cast.able.rejuvenation("target") and unit.friend("target") and unit.hp("target") <= ui.value("Rejuvenation")
+ and unit.player("target") and buff.rejuvenation.refresh("target")
+ then
+ return true
+ end
+ if cast.able.rejuvenation("player") and not unit.friend("target") and unit.hp() <= ui.value("Rejuvenation")
+ and buff.rejuvenation.refresh("player") and not buff.clearcasting.exists()
+ and (unit.solo() or ui.checked("Self Heal In Group"))
+ then
+ return true
+ end
+ end
+ if ui.checked("Healing Touch") and not cast.current.healingTouch() and not unit.moving()
+ and (freeHeal or noShapeshiftPower)
+ then
+ if cast.able.healingTouch("target") and unit.friend("target") and unit.hp("target") <= ui.value("Healing Touch") and unit.player("target") then
+ return true
+ end
+ if cast.able.healingTouch("player") and not unit.friend("target") and unit.hp() <= ui.value("Healing Touch")
+ and (not unit.inGroup() or ui.checked("Self Heal In Group"))
+ then
+ return true
+ end
+ end
+ end
+
+ return false
+end
+
+--------------------
+--- Action Lists ---
+--------------------
+-- Action List - Extra
+actionList.Extra = function()
+ -- Cancel Form to Interact with NPCs
+ -- if unit.exists("target") and unit.friend("target") and not unit.deadOrGhost("target") and not unit.player("target")
+ -- and unit.distance("target") < 8 and (buff.catForm.exists() or buff.bearForm.exists())
+ -- then
+ -- br._G.RunMacroText("/CancelForm")
+ -- ui.debug("Cancel Form [NPC]")
+ -- br._G.InteractUnit("target")
+ -- ui.debug("Interacting [NPC]")
+ -- return true
+ -- end
+
+ -- Aquatic Form
+ if ui.checked("Aquatic Form") and not unit.inCombat() and cast.able.aquaticForm()
+ and br._G.IsSwimming() and not buff.aquaticForm.exists() and not buff.prowl.exists()
+ and unit.moving() and timeMoving() > 2 and not inAggroRange()
+ and not (buff.markOfTheWild.refresh() and ui.checked("Mark of the Wild"))
+ and not (buff.thorns.refresh() and ui.checked("Thorns"))
+ then
+ if buff.catForm.exists() or buff.bearForm.exists() or buff.travelForm.exists() then
+ if safeCancelForm(spell.aquaticForm, "Swimming") then
+ return true
+ end
+ end
+ if cast.aquaticForm() then
+ ui.debug("Casting Aquatic Form")
+ return true
+ end
+ end
+
+ -- Travel Form
+ if ui.checked("Travel Form") and not unit.inCombat() and not br._G.IsMounted()
+ and not br._G.IsSwimming() and not br._G.IsFalling() and cast.able.travelForm()
+ and not buff.travelForm.exists() and not buff.prowl.exists()
+ and unit.moving() and timeMoving() > 2 and not inAggroRange()
+ and not (buff.markOfTheWild.refresh() and ui.checked("Mark of the Wild"))
+ and not (buff.thorns.refresh() and ui.checked("Thorns"))
+ then
+ if buff.catForm.exists() or buff.bearForm.exists() then
+ if safeCancelForm(spell.travelForm, "Travel") then
+ return true
+ end
+ end
+ if cast.travelForm() then
+ ui.debug("Casting Travel Form")
+ return true
+ end
+ end
+
+ -- Powershift for Energy (Cat Form with Furor talent)
+ if ui.checked("Powershifting") and cast.able.catForm() and buff.catForm.exists() and unit.inCombat() and energy < 30
+ and comboPoints < 5 and not buff.clearcasting.exists() and unit.power.mana.percent() > 30
+ then
+ if powershift() then
+ ui.debug("Powershift for Energy")
+ return true
+ end
+ end
+
+ -- Keep Aquatic Form while swimming unless danger/enemy target.
+ local keepAquatic = ui.checked("Aquatic Form")
+ and br._G.IsSwimming()
+ and buff.aquaticForm.exists()
+ and not inAggroRange()
+ and (not unit.exists("target") or unit.friend("target"))
+
+ -- Auto Shapeshift
+ if not keepAquatic and not needsFormBreak()
+ and ((unit.moving() and timeMoving() > ui.value("Shift Wait Time")) or unit.inCombat() or inAggroRange(5))
+ then
+ local formValue = ui.mode.forms
+ -- Bear Form
+ if formValue == 3 and unit.level() >= 8 and cast.able.bearForm() and not buff.bearForm.exists()
+ and not (buff.markOfTheWild.refresh() and ui.checked("Mark of the Wild"))
+ and not (buff.thorns.refresh() and ui.checked("Thorns"))
+ then
+ if cast.bearForm() then
+ ui.debug("Casting Bear Form")
+ return true
+ end
+ end
+ -- Caster Form
+ if formValue == 1 and (buff.bearForm.exists() or buff.catForm.exists()) then
+ if unit and unit.cancelForm then
+ unit.cancelForm()
+ else
+ br._G.RunMacroText("/CancelForm")
+ end
+ ui.debug("Casting Caster Form")
+ return true
+ end
+ -- Cat Form
+ if (formValue == 2 or (formValue == 3 and unit.level() < 8)) and unit.level() >= 5
+ and cast.able.catForm() and not buff.catForm.exists()
+ and not (buff.markOfTheWild.refresh() and ui.checked("Mark of the Wild"))
+ and not (buff.thorns.refresh() and ui.checked("Thorns"))
+ then
+ if cast.catForm() then
+ ui.debug("Casting Cat Form")
+ return true
+ end
+ end
+ end
+ -- Mark of the Wild
+ if ui.checked("Mark of the Wild") then
+ var.markUnit = getBuffUnitOption("Mark of the Wild")
+ if cast.able.markOfTheWild(var.markUnit) and buff.markOfTheWild.refresh(var.markUnit)
+ and not unit.inCombat() and not unit.resting() and unit.distance(var.markUnit) < 40
+ and (var.markUnit == "player" or unit.player(var.markUnit))
+ then
+ -- Cancel form if needed to buff
+ local needsCancel = buff.catForm.exists() or buff.bearForm.exists() or buff.travelForm.exists() or buff.aquaticForm.exists()
+ if needsCancel then
+ if safeCancelForm(spell.markOfTheWild, "Mark of the Wild") then
+ return true
+ end
+ else
+ if cast.markOfTheWild(var.markUnit) then
+ ui.debug("Casting Mark of the Wild")
+ return true
+ end
+ end
+ end
+ end
+ -- Thorns
+ if ui.checked("Thorns") then
+ var.thornUnit = getBuffUnitOption("Thorns")
+ if cast.able.thorns(var.thornUnit) and buff.thorns.refresh(var.thornUnit)
+ and not unit.inCombat() and not unit.resting() and unit.distance(var.thornUnit) < 40
+ and (var.thornUnit == "player" or unit.player(var.thornUnit))
+ then
+ -- Cancel form if needed to buff
+ local needsCancel = buff.catForm.exists() or buff.bearForm.exists() or buff.travelForm.exists() or buff.aquaticForm.exists()
+ if needsCancel then
+ if safeCancelForm(spell.thorns, "Thorns") then
+ return true
+ end
+ else
+ if cast.thorns(var.thornUnit) then
+ ui.debug("Casting Thorns")
+ return true
+ end
+ end
+ end
+ end
+ -- Omen of Clarity
+ if ui.value("Omen of Clarity") ~= 4 and not unit.inCombat() then
+ if cast.able.omenOfClarity("player") and buff.omenOfClarity.refresh("player") then
+ -- Cancel form if needed to buff
+ local needsCancel = buff.catForm.exists() or buff.bearForm.exists() or buff.travelForm.exists() or buff.aquaticForm.exists()
+ if needsCancel then
+ if safeCancelForm(spell.omenOfClarity, "Omen of Clarity") then
+ return true
+ end
+ else
+ if cast.omenOfClarity("player") then
+ ui.debug("Casting Omen of Clarity")
+ return true
+ end
+ end
+ end
+ end
+end -- End Action List - Extra
+
+-- Action List - Defensive
+actionList.Defensive = function()
+ if ui.useDefensive() then
+ -- Abolish Poison
+ if ui.checked("Abolish Poison") and cast.able.abolishPoison("player")
+ and not buff.abolishPoison.exists("player") and unit.hasDispel("player", spell.abolishPoison)
+ then
+ if buff.catForm.exists() or buff.bearForm.exists() then
+ if safeCancelForm(spell.abolishPoison, "Abolish Poison") then
+ return true
+ end
+ else
+ if cast.abolishPoison("player") then
+ ui.debug("Casting Abolish Poison")
+ return true
+ end
+ end
+ end
+ -- Cure Poison
+ if ui.checked("Cure Poison") and cast.able.curePoison("player")
+ and (not ui.checked("Abolish Poison") --[[or not spell.known.abolishPoison()]]) and unit.hasDispel("player", spell.curePoison)
+ then
+ if buff.catForm.exists() or buff.bearForm.exists() then
+ if safeCancelForm(spell.curePoison, "Cure Poison") then
+ return true
+ end
+ else
+ if cast.curePoison("player") then
+ ui.debug("Casting Cure Poison")
+ return true
+ end
+ end
+ end
+ -- Remove Curse
+ if ui.checked("Remove Curse") and cast.able.removeCurse("player")
+ and not unit.inCombat() and unit.hasDispel("player", spell.removeCurse)
+ then
+ if buff.catForm.exists() or buff.bearForm.exists() then
+ if safeCancelForm(spell.removeCurse, "Remove Curse") then
+ return true
+ end
+ else
+ if cast.removeCurse("player") then
+ ui.debug("Casting Remove Curse")
+ return true
+ end
+ end
+ end
+ -- Barkskin
+ if ui.checked("Barkskin") and cast.able.barkskin()
+ and unit.hp() <= ui.value("Barkskin") and unit.inCombat()
+ then
+ if cast.barkskin() then
+ ui.debug("Casting Barkskin")
+ return true
+ end
+ end
+ -- Entangling Roots (fleeing enemies)
+ if ui.checked("Entangling Roots") and cast.able.entanglingRoots("target")
+ and unit.exists("target") and not unit.facing("target", "player") and unit.moving("target")
+ and unit.valid("target") and unit.hp("target") < 100 and not debuff.entanglingRoots.exists("target")
+ and unit.distance("target") > 8 and unit.inCombat()
+ then
+ if buff.catForm.exists() or buff.bearForm.exists() then
+ if safeCancelForm(spell.entanglingRoots, "Entangling Roots", freeDPS) then
+ return true
+ end
+ else
+ if cast.entanglingRoots("target") then
+ ui.debug("Casting Entangling Roots")
+ return true
+ end
+ end
+ end
+ -- Faerie Fire (Caster)
+ if ui.checked("Faerie Fire") and cast.able.faerieFire("target")
+ and unit.exists("target") and unit.enemy("target") and unit.canAttack("target") and not debuff.faerieFire.exists("target")
+ and not (buff.catForm.exists() or buff.bearForm.exists())
+ and unit.distance("target") > 8 and noShapeshiftPower
+ then
+ if cast.faerieFire("target") then
+ ui.debug("Casting Faerie Fire")
+ return true
+ end
+ end
+ -- Nature's Grasp
+ if ui.checked("Nature's Grasp") and cast.able.naturesGrasp()
+ and not buff.naturesGrasp.exists("player") and unit.inCombat()
+ and #enemies.yards10 > 0
+ then
+ if buff.catForm.exists() or buff.bearForm.exists() then
+ if safeCancelForm(spell.naturesGrasp, "Nature's Grasp", freeDPS) then
+ return true
+ end
+ else
+ if cast.naturesGrasp("player") then
+ ui.debug("Casting Nature's Grasp")
+ return true
+ end
+ end
+ end
+ -- Regrowth
+ if ui.checked("Regrowth") and cast.able.regrowth("player") and not cast.current.regrowth() and not unit.moving()
+ and unit.hp() <= ui.value("Regrowth") and not buff.regrowth.exists("player")
+ and (freeHeal or noShapeshiftPower)
+ and (not unit.inGroup() or ui.checked("Self Heal In Group"))
+ then
+ if buff.catForm.exists() or buff.bearForm.exists() then
+ if safeCancelForm(spell.regrowth, "Regrowth", freeHeal) then
+ return true
+ end
+ else
+ if cast.regrowth("player") then
+ ui.debug("Casting Regrowth")
+ return true
+ end
+ end
+ end
+ -- Rejuvenation
+ if ui.checked("Rejuvenation") and not cast.current.rejuvenation("target") then
+ if cast.able.rejuvenation("target") and unit.friend("target") and unit.hp("target") <= ui.value("Rejuvenation")
+ and unit.player("target") and buff.rejuvenation.refresh("target")
+ then
+ if buff.catForm.exists() or buff.bearForm.exists() then
+ if safeCancelForm(spell.rejuvenation, "Rejuvenation", freeHeal) then
+ return true
+ end
+ else
+ ui.debug("Attempting to cast Rejuvenation on " .. unit.name("target"))
+ if cast.rejuvenation("target") then
+ return true
+ end
+ end
+ end
+ if cast.able.rejuvenation("player") and not unit.friend("target") and unit.hp() <= ui.value("Rejuvenation")
+ and buff.rejuvenation.refresh("player") and not buff.clearcasting.exists()
+ and (unit.solo() or ui.checked("Self Heal In Group"))
+ then
+ if buff.catForm.exists() or buff.bearForm.exists() then
+ if safeCancelForm(spell.rejuvenation, "Rejuvenation", freeHeal) then
+ return true
+ end
+ else
+ if cast.rejuvenation("player") then
+ ui.debug("Casting Rejuvenation on " .. unit.name("player"))
+ return true
+ end
+ end
+ end
+ end
+ -- Healing Touch
+ if ui.checked("Healing Touch") and not cast.current.healingTouch() and not unit.moving()
+ and (freeHeal or noShapeshiftPower)
+ then
+ if cast.able.healingTouch("target") and unit.friend("target") and unit.hp("target") <= ui.value("Healing Touch") and unit.player("target") then
+ if buff.catForm.exists() or buff.bearForm.exists() then
+ if safeCancelForm(spell.healingTouch, "Healing Touch", freeHeal) then
+ return true
+ end
+ else
+ if cast.healingTouch("target") then
+ ui.debug("Casting Healing Touch on " .. unit.name("target"))
+ return true
+ end
+ end
+ end
+ if cast.able.healingTouch("player") and not unit.friend("target") and unit.hp() <= ui.value("Healing Touch")
+ and (not unit.inGroup() or ui.checked("Self Heal In Group"))
+ then
+ if buff.catForm.exists() or buff.bearForm.exists() then
+ if safeCancelForm(spell.healingTouch, "Healing Touch", freeHeal) then
+ return true
+ end
+ else
+ if cast.healingTouch("player") then
+ ui.debug("Casting Healing Touch on " .. unit.name("player"))
+ return true
+ end
+ end
+ end
+ end
+ -- -- Health Potion
+ -- if ui.checked("Health Potion") and unit.canUseItem(5512)
+ -- and unit.hp() < ui.value("Rejuvenation")
+ -- and (not unit.inGroup() or ui.checked("Self Heal In Group"))
+ -- then
+ -- if buff.catForm.exists() or buff.bearForm.exists() then
+ -- br._G.RunMacroText("/CancelForm")
+ -- ui.debug("Cancel Form for Health Potion")
+ -- return true
+ -- end
+ -- if unit.useItem(5512) then
+ -- ui.debug("Using Health Potion")
+ -- return true
+ -- end
+ -- end
+ -- -- Mana Potion
+ -- if ui.checked("Mana Potion") and unit.canUseItem(2455)
+ -- and unit.power.mana.percent() < 30
+ -- then
+ -- if buff.catForm.exists() or buff.bearForm.exists() then
+ -- br._G.RunMacroText("/CancelForm")
+ -- ui.debug("Cancel Form for Mana Potion")
+ -- return true
+ -- end
+ -- if unit.useItem(2455) then
+ -- ui.debug("Using Mana Potion")
+ -- return true
+ -- end
+ -- end
+ -- Bash (low HP defensive stun)
+ if ui.checked("Bash") and unit.hp() <= ui.value("Bash") then
+ if (buff.bearForm.exists() or buff.direBearForm.exists()) and cast.able.bash("target") then
+ if cast.bash("target") then
+ ui.debug("Casting Bash [Defensive]")
+ return true
+ end
+ end
+ end
+ end
+end -- End Action List - Defensive
+
+-- * Action List - Interrupts
+actionList.Interrupts = function()
+ if ui.useInterrupt() and ui.delay("Interrupts", unit.gcd(true))
+ -- and not (buff.prowl.exists() or buff.shadowmeld.exists())
+ then
+ local thisUnit
+ -- * Bash
+ if ui.checked("Bash - Int") then
+ for i = 1, #enemies.yards5f do
+ thisUnit = enemies.yards5f[i]
+ if unit.interruptable(thisUnit, ui.value("Interrupt At")) then
+ -- Bash is Bear Form only in Classic; shift first if needed.
+ if not buff.bearForm.exists() and cast.able.bearForm() then
+ if cast.bearForm() then
+ ui.debug("Shifting to Bear Form for Bash interrupt")
+ return true
+ end
+ end
+ if (buff.bearForm.exists() or buff.direBearForm.exists()) and cast.able.bash(thisUnit) then
+ if cast.bash(thisUnit) then
+ ui.debug("Casting Bash on " .. unit.name(thisUnit))
+ return true
+ end
+ end
+ end
+ end
+ end
+ end -- End useInterrupts check
+end -- End Action List - Interrupts
+
+-- Action List - Cooldowns
+actionList.Cooldowns = function()
+ if ui.useCDs() then
+ -- Innervate
+ if ui.checked("Self-Innervate") and cast.able.innervate()
+ and unit.power.mana.percent() < 30
+ then
+ if cast.innervate("player") then
+ ui.debug("Casting Innervate")
+ return true
+ end
+ end
+ end
+end -- End Action List - Cooldowns
+
+-- Action List - Bear Form
+actionList.BearForm = function()
+ -- Enrage
+ if cast.able.enrage() and not unit.deadOrGhost("target") and not buff.enrage.exists() and rage < 10 and unit.hp() > 80 then
+ if cast.enrage() then
+ ui.debug("Casting Enrage")
+ return true
+ end
+ end
+ -- Demoralizing Roar
+ if cast.able.demoralizingRoar() and not debuff.demoralizingRoar.exists(units.dyn5)
+ and #enemies.yards10 > 0
+ then
+ if cast.demoralizingRoar() then
+ ui.debug("Casting Demoralizing Roar")
+ return true
+ end
+ end
+ -- Swipe (multi-target)
+ if cast.able.swipe() and ui.useAOE(8,3) then
+ if cast.swipe() then
+ ui.debug("Casting Swipe")
+ return true
+ end
+ end
+ -- Maul
+ if cast.able.maul(units.dyn5) and (ui.useST(8,3) or not spell.swipe.known()) then
+ if cast.maul(units.dyn5) then
+ ui.debug("Casting Maul")
+ return true
+ end
+ end
+end -- End Action List - Bear Form
+
+-- Action List - Cat Opener (Stealth + Non-Stealth)
+actionList.CatOpener = function()
+ if not buff.catForm.exists() then return false end
+ if not unit.valid("target") or unit.deadOrGhost("target") or unit.distance("target") >= 5 then return false end
+ if unit.player("target") then return false end
+
+ -- Tiger's Fury gating: if enabled+known and we're at full energy, cast it before opening.
+ if ui.checked("Tiger's Fury") and spell.tigersFury.known() and energy == 100 and not buff.tigersFury.exists() then
+ if cast.able.tigersFury() and cast.tigersFury() then
+ ui.debug("Casting Tiger's Fury [Opener]")
+ return true
+ end
+ return false
+ end
+
+ local opener = ui.value("Cat Opener")
+
+ -- Stealth Opener
+ if buff.prowl.exists() then
+ -- Pounce (Opener 1)
+ if opener == 1 and cast.able.pounce() then
+ if cast.pounce("target") then
+ ui.debug("Casting Pounce [Stealth Opener]")
+ return true
+ end
+ end
+
+ -- Ravage from behind (Opener 2 or fallback)
+ if (opener == 2 or (opener <= 1 and not spell.pounce.known()))
+ and cast.able.ravage() and isBehind("target", "player")
+ then
+ if cast.ravage("target") then
+ ui.debug("Casting Ravage [Stealth Opener]")
+ return true
+ end
+ end
+
+ -- Shred from behind (Opener 3 or fallback)
+ if (opener == 3 or (opener <= 2 and not spell.ravage.known()))
+ and cast.able.shred() and isBehind("target", "player") and not unit.facing("target", "player")
+ then
+ if cast.shred("target") then
+ ui.debug("Casting Shred [Stealth Opener]")
+ return true
+ end
+ end
+
+ -- Rake (Opener 4 or fallback)
+ if ui.checked("Rake") and (opener == 4 or (opener <= 3 and not spell.shred.known()))
+ and cast.able.rake()
+ then
+ if cast.rake("target") then
+ ui.debug("Casting Rake [Stealth Opener]")
+ return true
+ end
+ end
+
+ -- Claw (ultimate fallback)
+ if ((opener == 4 and not ui.checked("Rake")) or (opener < 4 and not spell.rake.known())) and cast.able.claw() then
+ if cast.claw("target") then
+ ui.debug("Casting Claw [Stealth Opener]")
+ return true
+ end
+ end
+
+ return false
+ end
+
+ -- Non-Stealth Opener
+ local behindTarget = isBehind("target", "player")
+ if (behindTarget or not spell.shred.known()) then
+ -- Shred (from behind)
+ local triedShred = false
+ if behindTarget and cast.able.shred() then
+ if not unit.facing("target", "player") then
+ triedShred = true
+ if cast.shred("target") then
+ ui.debug("Casting Shred [Opener]")
+ return true
+ end
+ end
+ end
+ -- Claw (fallback)
+ if cast.able.claw() and (not spell.shred.known() or not behindTarget) then
+ if cast.claw("target") then
+ ui.debug("Casting Claw [Opener]")
+ return true
+ end
+ end
+ end
+
+ return false
+end
+
+-- Action List - Cat Form
+actionList.CatForm = function()
+ -- Ferocious Bite - Finish Him!
+ local finish = ferociousBiteFinish(units.dyn5)
+ if cast.able.ferociousBite(units.dyn5) and energy >= 35 and finish then
+ if cast.ferociousBite(units.dyn5) then
+ ui.debug("Casting Ferocious Bite [Finish Him!]")
+ return true
+ end
+ end
+
+ -- Tiger's Fury
+ if ui.checked("Tiger's Fury") and cast.able.tigersFury() and buff.catForm.exists() and energy == 100
+ and not buff.tigersFury.exists()
+ then
+ if cast.tigersFury() then
+ ui.debug("Casting Tiger's Fury")
+ return true
+ end
+ end
+
+ -- Faerie Fire (Feral)
+ if ui.checked("Faerie Fire") and cast.able.faerieFireFeral() and unit.enemy(units.dyn5) and unit.canAttack(units.dyn5)
+ and not debuff.faerieFireFeral.exists(units.dyn5)
+ and not unit.isElemental(units.dyn5)
+ and unit.ttd(units.dyn5) > cd.global.remain()
+ then
+ if cast.faerieFireFeral() then
+ ui.debug("Casting Faerie Fire (Feral)")
+ return true
+ end
+ end
+
+ -- 5 Combo Points - Finishers
+ if comboPoints == 5 and not finish then
+ -- Ferocious Bite (low energy)
+ if cast.able.ferociousBite(units.dyn5) and energy >= 35 and energy < 60 then
+ if cast.ferociousBite(units.dyn5) then
+ ui.debug("Casting Ferocious Bite")
+ return true
+ end
+ end
+ -- Rip
+ if ui.checked("Rip") and cast.able.rip() and unit.ttd(units.dyn5) > 6 and debuff.rip.refresh(units.dyn5) then
+ if cast.rip(units.dyn5) then
+ ui.debug("Casting Rip")
+ return true
+ end
+ end
+ end
+
+ -- Combo Point Builders
+ if (comboPoints < 5 or energy >= 60 or freeDPS) and not finish then
+ -- Ravage (from Prowl)
+ local behindRavage = isBehind(units.dyn5, "player")
+ if behindRavage then debugBehind("Ravage behind=true", units.dyn5) end
+ if cast.able.ravage() and buff.prowl.exists() and behindRavage then
+ if cast.ravage(units.dyn5) then
+ ui.debug("Casting Ravage")
+ return true
+ end
+ end
+ -- Rake
+ if ui.checked("Rake") and cast.able.rake() and debuff.rake.refresh(units.dyn5)
+ and (unit.ttd(units.dyn5) <= 3 or not spell.ferociousBite.known())
+ and not freeDPS
+ then
+ if cast.rake(units.dyn5) then
+ ui.debug("Casting Rake")
+ return true
+ end
+ end
+ -- Shred (from behind)
+ local behindDyn5 = isBehind(units.dyn5, "player")
+ if behindDyn5 then debugBehind("Shred behind=true", units.dyn5) end
+
+ local triedShred = false
+ if behindDyn5 and cast.able.shred() then
+ if not unit.facing(units.dyn5, "player") then
+ triedShred = true
+ if cast.shred(units.dyn5) then
+ ui.debug("Casting Shred")
+ return true
+ end
+ end
+ end
+ -- Claw (not behind)
+ if cast.able.claw() and (not spell.shred.known() or not behindDyn5) then
+ if cast.claw(units.dyn5) then
+ ui.debug("Casting Claw")
+ return true
+ end
+ end
+ end
+end -- End Action List - Cat Form
+
+-- Action List - Caster Form
+actionList.CasterForm = function()
+ -- Moonfire
+ if cast.able.moonfire(units.dyn40AOE) and debuff.moonfire.refresh(units.dyn40AOE) then
+ if cast.moonfire(units.dyn40AOE) then
+ ui.debug("Casting Moonfire")
+ return true
+ end
+ end
+ -- Starfire (if available)
+ if cast.able.starfire() and not unit.moving() and cast.able.starfire() and unit.level() >= 20
+ and (debuff.moonfire.exists(units.dyn40AOE) or not spell.moonfire.known())
+ and (not cast.last.starfire() or cast.timeSinceLast.starfire() > unit.gcd(true) + 0.5)
+ then
+ if cast.starfire() then
+ ui.debug("Casting Starfire")
+ return true
+ end
+ end
+ -- Wrath
+ if cast.able.wrath() and not unit.moving()
+ and (unit.level() < 20 or not spell.starfire.known())
+ and (debuff.moonfire.exists(units.dyn40AOE) or not spell.moonfire.known())
+ and (not cast.last.wrath() or cast.timeSinceLast.wrath() > unit.gcd(true) + 0.5)
+ then
+ if cast.wrath() then
+ ui.debug("Casting Wrath")
+ return true
+ end
+ end
+end -- End Action List - Caster Form
+
+-- Action List - Pre-Combat
+actionList.PreCombat = function()
+ if not unit.inCombat() and not (unit.flying() or unit.mounted()) then
+ if not (buff.prowl.exists() or buff.shadowmeld.exists()) then
+ -- Prowl
+ if ui.checked("Prowl") and cast.able.prowl("player") and buff.catForm.exists() and autoProwl() and ui.mode.prowl == 1
+ and not buff.prowl.exists() and not unit.resting()
+ then
+ if cast.prowl("player") then
+ ui.debug("Casting Prowl [Precombat]")
+ return true
+ end
+ end
+ end -- End No Stealth
+ if unit.valid("target") then
+ local formValue = ui.mode.forms
+ local thisDistance = unit.distance("target") or 99
+ -- Wrath
+ if not unit.moving() and not (buff.catForm.exists() or buff.bearForm.exists()) and thisDistance < 30 then
+ if cast.able.wrath("target") and (unit.level() < 2 or not cast.last.wrath() or cast.timeSinceLast.wrath() > unit.gcd(true) + 0.5) then
+ if cast.wrath("target") then
+ ui.debug("Casting Wrath [Precombat]")
+ return true
+ end
+ end
+ end
+ if thisDistance < 5 then
+ -- Cat Opener (Stealth + Non-Stealth)
+ if actionList.CatOpener() then return true end
+ -- Bear Form
+ if formValue == 3 and buff.bearForm.exists() then
+ -- Enrage
+ if cast.able.enrage() and not unit.deadOrGhost("target") and not buff.enrage.exists() and rage < 10 then
+ if cast.enrage() then
+ ui.debug("Casting Enrage [Precombat]")
+ return true
+ end
+ end
+ -- Swipe
+ if cast.able.swipe() and ui.useAOE(8,3) then
+ if cast.swipe() then
+ ui.debug("Casting Swipe [Precombat]")
+ return true
+ end
+ end
+ -- Maul
+ if cast.able.maul(units.dyn5) and (ui.useST(8,3) or not spell.swipe.known()) then
+ if cast.maul(units.dyn5) then
+ ui.debug("Casting Maul [Precombat]")
+ return true
+ end
+ end
+ end
+ -- Auto Attack (do not break Prowl)
+ if not buff.prowl.exists() and not cast.auto.autoAttack() and unit.valid("target") and not unit.deadOrGhost("target") and unit.distance("target") < 5 then
+ StartAttack()
+ ui.debug("Casting Auto Attack [Precombat]")
+ return true
+ end
+ end
+ end
+ end
+end -- End Action List - PreCombat
+
+-- Action List - Combat
+actionList.Combat = function()
+ if unit.inCombat() and unit.valid("target") then
+ local formValue = ui.mode.forms
+ ------------------------
+ --- In Combat - Main ---
+ ------------------------
+ -- Start Attack (do not break Prowl)
+ if not buff.prowl.exists() and unit.exists(units.dyn5) and not cast.auto.autoAttack() and not unit.deadOrGhost("target") then
+ StartAttack()
+ ui.debug("Casting Auto Attack")
+ return true
+ end
+
+ -- Wait for swing if in melee range
+ if unit.exists(units.dyn5) and unit.distance(units.dyn5) < 5 then
+ local swing = br.swingTimer
+ -- ui.debug("Swing Timer: " .. tostring(swing) .. " | Distance: " .. tostring(unit.distance(units.dyn5)))
+ if swing and swing > 0 and swing < 0.3 then
+ -- ui.debug("Waiting for swing: " .. br.functions.misc:round2(swing) .. "s")
+ return true
+ end
+ end
+
+ -- Call Action List - Cooldowns
+ if actionList.Cooldowns() then return true end
+
+ -- Last Form restoration
+ if ui.checked("Last Form") and lastForm ~= nil
+ and (((unit.moving() and timeMoving() > 2 and (not spell.travelForm.known() or not ui.checked("Travel Form")))
+ or (unit.inCombat() or inAggroRange(5)) and ((unit.exists("target") and not unit.friend("target")) or not unit.exists("target")))
+ and not lastFormBuff and not (buff.aquaticForm.exists() or buff.travelForm.exists() or inAggroRange(5)))
+ then
+ if cast.able.catForm() and lastForm == spell.catForm then
+ if cast.catForm() then
+ ui.debug("Casting Last Form [Cat]")
+ return true
+ end
+ end
+ if cast.able.bearForm() and (lastForm == spell.bearForm or lastForm == spell.direBearForm) then
+ if cast.bearForm() then
+ ui.debug("Casting Last Form [Bear]")
+ return true
+ end
+ end
+ end
+
+ -- Call Action List - Bear Form
+ if formValue == 3 and buff.bearForm.exists() and unit.exists(units.dyn5) and unit.distance(units.dyn5) < 5 then
+ if actionList.BearForm() then return true end
+ end
+
+ -- Call Action List - Cat Form
+ if formValue == 2 and buff.catForm.exists() and unit.exists(units.dyn5) and unit.distance(units.dyn5) < 5 then
+ if buff.prowl.exists() and actionList.CatOpener() then return true end
+ if actionList.CatForm() then return true end
+ end
+
+ -- Call Action List - Caster Form
+ if formValue == 1 and not (buff.catForm.exists() or buff.bearForm.exists()) then
+ if actionList.CasterForm() then return true end
+ end
+ end -- End In Combat Rotation
+end -- End Action list - Combat
+
+----------------
+--- ROTATION ---
+----------------
+local function runRotation()
+ ---------------------
+ --- Define Locals ---
+ ---------------------
+ -- BR API Locals
+ buff = br.player.buff
+ cast = br.player.cast
+ cd = br.player.cd
+ comboPoints = br.player.power.comboPoints()
+ debuff = br.player.debuff
+ enemies = br.player.enemies
+ energy = br.player.power.energy()
+ rage = br.player.power.rage()
+ module = br.player.module
+ ui = br.player.ui
+ unit = br.player.unit
+ units = br.player.units
+ spell = br.player.spell
+ -- General Locals
+ profileStop = profileStop or false
+ haltProfile = (unit.inCombat() and profileStop) or ui.pause() or ui.mode.rotation == 4 or unit.id("target") == 156716
+ -- Units
+ units.get(5) -- Makes a variable called, units.dyn5
+ units.get(40) -- Makes a variable called, units.dyn40
+ units.get(40, true) -- Makes a variable called, units.dyn40AOE
+ -- Enemies
+ enemies.get(5) -- Makes a varaible called, enemies.yards5
+ enemies.get(20) -- Makes a varaible called, enemies.yards20
+ enemies.get(20, "player", true) -- makes enemies.yards20nc
+ enemies.get(40) -- Makes a varaible called, enemies.yards40
+
+ -- Profile Specific Locals
+ fbMaxEnergy = energy >= 50
+
+ -- Update Last Form tracking
+ updateLastForm()
+
+ -- Omen of Clarity Logic
+ local omenSetting = ui.value("Omen of Clarity")
+ needsHealing = (ui.checked("Regrowth") and unit.hp() <= ui.value("Regrowth"))
+ or (ui.checked("Healing Touch") and unit.hp() <= ui.value("Healing Touch"))
+ or (ui.checked("Rejuvenation") and unit.hp() <= ui.value("Rejuvenation"))
+
+ freeDPS = omenSetting ~= 4 and omenSetting ~= 2 and buff.clearcasting.exists()
+ and (not needsHealing or omenSetting == 3)
+
+ freeHeal = omenSetting ~= 4 and omenSetting ~= 3 and buff.clearcasting.exists()
+
+ -- No Shapeshift Power check
+ noShapeshiftPower = ((not buff.catForm.exists() or (buff.catForm.exists() and energy < ui.value("Energy")))
+ and (not buff.bearForm.exists() or (buff.bearForm.exists() and rage < ui.value("Rage"))))
+ or not unit.inCombat()
+
+ if not unit.inCombat() and not unit.exists("target") then
+ if profileStop then profileStop = false end
+ end
+
+ ---------------------
+ --- Begin Profile ---
+ ---------------------
+ -- Profile Stop | Pause
+ if not unit.inCombat() and not unit.exists("target") and profileStop then
+ profileStop = false
+ elseif haltProfile then
+ return true
+ else
+ ---------------------------------
+ --- Out Of Combat - Rotations ---
+ ---------------------------------
+ -----------------------
+ --- Extras Rotation ---
+ -----------------------
+ if actionList.Extra() then return true end
+ -----------------
+ --- Defensive ---
+ -----------------
+ if actionList.Defensive() then return true end
+ ------------------
+ --- Pre-Combat ---
+ ------------------
+ if actionList.PreCombat() then return true end
+ -----------------------------
+ --- In Combat - Rotations ---
+ -----------------------------
+ if actionList.Combat() then return true end
+ end -- Pause
+ return true
+end -- End runRotation
+local id = 283 -- Change to the spec id profile is for.
+local expansion = br.isClassic -- Change to the expansion the profile is for.
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
+ name = rotationName,
+ toggles = createToggles,
+ options = createOptions,
+ run = runRotation,
+})
diff --git a/Rotations/Druid/Feral/FeralCuteOne-MOP.lua b/Rotations/Druid/Feral/FeralCuteOne-MOP.lua
new file mode 100644
index 0000000000..c0dfad7909
--- /dev/null
+++ b/Rotations/Druid/Feral/FeralCuteOne-MOP.lua
@@ -0,0 +1,1961 @@
+-------------------------------------------------------
+-- Author = CuteOne
+-- Patch = 11.0.2
+-- Patch should be the latest patch you've updated the rotation for (i.e., 9.2.5)
+-- Coverage = 100%
+-- Coverage should be your estimated percent coverage for class mechanics (i.e., 100%)
+-- Status = Full
+-- Status should be one of: Full, Limited, Sporadic, Inactive, Unknown
+-- Readiness = Untested
+-- Readiness should be one of: Raid, NoRaid, Basic, Development, Untested
+-------------------------------------------------------
+-- Required: Fill above fields to populate README.md --
+-------------------------------------------------------
+local rotationName = "CuteOne"
+---------------
+--- Toggles ---
+---------------
+local function createToggles()
+ -- Rotation Button
+ local RotationModes = {
+ [1] = { mode = "Auto", value = 1, overlay = "Automatic Rotation", tip = "Swaps between Single and Multiple based on number of targets in range.", highlight = 1, icon = br.player.spells.swipe },
+ [2] = { mode = "Mult", value = 2, overlay = "Multiple Target Rotation", tip = "Multiple target rotation used.", highlight = 0, icon = br.player.spells.swipe },
+ [3] = { mode = "Sing", value = 3, overlay = "Single Target Rotation", tip = "Single target rotation used.", highlight = 0, icon = br.player.spells.mangle },
+ [4] = { mode = "Off", value = 4, overlay = "DPS Rotation Disabled", tip = "Disable DPS Rotation", highlight = 0, icon = br.player.spells.rejuvenation }
+ };
+ br.ui:createToggle(RotationModes, "Rotation", 1, 0)
+ -- Cooldown Button
+ local CooldownModes = {
+ [1] = { mode = "Auto", value = 1, overlay = "Cooldowns Automated", tip = "Automatic Cooldowns - Boss Detection.", highlight = 1, icon = br.player.spells.berserk },
+ [2] = { mode = "On", value = 2, overlay = "Cooldowns Enabled", tip = "Cooldowns used regardless of target.", highlight = 0, icon = br.player.spells.berserk },
+ [3] = { mode = "Off", value = 3, overlay = "Cooldowns Disabled", tip = "No Cooldowns will be used.", highlight = 0, icon = br.player.spells.berserk }
+ };
+ br.ui:createToggle(CooldownModes, "Cooldown", 2, 0)
+ -- Defensive Button
+ local DefensiveModes = {
+ [1] = { mode = "On", value = 1, overlay = "Defensive Enabled", tip = "Includes Defensive Cooldowns.", highlight = 1, icon = br.player.spells.barkskin },
+ [2] = { mode = "Off", value = 2, overlay = "Defensive Disabled", tip = "No Defensives will be used.", highlight = 0, icon = br.player.spells.barkskin }
+ };
+ br.ui:createToggle(DefensiveModes, "Defensive", 3, 0)
+ -- Interrupt Button
+ local InterruptModes = {
+ [1] = { mode = "On", value = 1, overlay = "Interrupts Enabled", tip = "Includes Basic Interrupts.", highlight = 1, icon = br.player.spells.skullBash },
+ [2] = { mode = "Off", value = 2, overlay = "Interrupts Disabled", tip = "No Interrupts will be used.", highlight = 0, icon = br.player.spells.skullBash }
+ };
+ br.ui:createToggle(InterruptModes, "Interrupt", 4, 0)
+ -- Prowl Button
+ local ProwlModes = {
+ [1] = { mode = "On", value = 1, overlay = "Prowl Enabled", tip = "Rotation will use Prowl", highlight = 1, icon = br.player.spells.prowl },
+ [2] = { mode = "Off", value = 2, overlay = "Prowl Disabled", tip = "Rotation will not use Prowl", highlight = 0, icon = br.player.spells.prowl }
+ };
+ br.ui:createToggle(ProwlModes, "Prowl", 5, 0)
+ -- Boss Pull Button
+ local BossPullModes = {
+ [1] = { mode = "On", value = 1, overlay = "Boss Pre-Pull Active", tip = "Raid Boss Pre-Pull. Auto-disables on combat.", highlight = 1, icon = br.player.items.virmensBite },
+ [2] = { mode = "Off", value = 2, overlay = "Boss Pre-Pull Disabled", tip = "Normal rotation", highlight = 0, icon = br.player.items.virmensBite }
+ };
+ br.ui:createToggle(BossPullModes, "BossPull", 6, 0)
+end
+
+---------------
+--- OPTIONS ---
+---------------
+local function createOptions()
+ local optionTable
+
+ local function rotationOptions()
+ local section
+ -----------------------
+ --- General Options ---
+ -----------------------
+ section = br.ui:createSection(br.ui.window.profile, "General")
+ -- Death Cat
+ br.ui:createCheckbox(section, "Death Cat Mode",
+ "|cff15FF00Enable|cffFFFFFF/|cffD60000Disable |cffFFFFFFthis mode when running through low level content where you 1 hit kill mobs.")
+ -- Fire Cat
+ br.ui:createCheckbox(section, "Perma Fire Cat",
+ "|cff15FF00Enable|cffFFFFFF/|cffD60000Disable |cffFFFFFFautomatic use of Fandrel's Seed Pouch or Burning Seeds.")
+ -- Dummy DPS Test
+ br.ui:createSpinner(section, "DPS Testing", 5, 5, 60, 5,
+ "|cffFFFFFFSet to desired time for test in minuts. Min: 5 / Max: 60 / Interval: 5")
+ -- Ferocious Bite Execute
+ br.ui:createDropdownWithout(section, "Ferocious Bite Execute",
+ { "|cffFFFF00Enabled Notify", "|cff00FF00Enabled", "|cffFF0000Disabled" }, 2,
+ "Options for using Ferocious Bite when the damage from it will kill the unit.")
+ -- Pre-Pull Timer
+ br.ui:createSpinner(section, "Pre-Pull Timer", 5, 1, 10, 1,
+ "|cffFFFFFFSet to desired time to start Pre-Pull (DBM Required). Min: 1 / Max: 10 / Interval: 1")
+ -- Auto Shapeshifts
+ br.ui:createCheckbox(section, "Auto Shapeshifts",
+ "|cff15FF00Enables|cffFFFFFF/|cffD60000Disables |cffFFFFFFAuto Shapeshifting to best form for situation.|cffFFBB00.")
+ -- Fall Timer
+ br.ui:createSpinnerWithout(section, "Fall Timer", 2, 1, 5, 0.25,
+ "|cffFFFFFFSet to desired time to wait until shifting to Flight/Cat Form form when falling (in secs).")
+ -- Break Crowd Control
+ br.ui:createCheckbox(section, "Break Crowd Control",
+ "|cff15FF00Enables|cffFFFFFF/|cffD60000Disables |cffFFFFFFAuto Shapeshifting to break crowd control.|cffFFBB00.")
+ -- Wild Charge
+ br.ui:createCheckbox(section, "Wild Charge",
+ "|cff15FF00Enables|cffFFFFFF/|cffD60000Disables |cffFFFFFFAuto Charge usage.|cffFFBB00.")
+ -- Mark of the Wild
+ br.ui:createDropdown(section, "Mark of the Wild",
+ { "|cffFFFFFFPlayer", "|cffFFFFFFTarget", "|cffFFFFFFMouseover", "|cffFFFFFFFocus", "|cffFFFFFFGroup" }, 1,
+ "|cffFFFFFFSet how to use Mark of the Wild")
+ br.ui:checkSectionState(section)
+ ------------------------
+ --- Cooldown Options ---
+ ------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Cooldowns")
+ -- Tiger's Fury
+ br.ui:createCheckbox(section, "Tiger's Fury")
+ -- Berserk / Incarnation: King of the Jungle
+ br.ui:createDropdownWithout(section, "Berserk/Incarnation", br.ui.dropOptions.AlwaysCdAoeNever,
+ 2, "|cffFFFFFFSet when to use Berserk/Incarnation")
+ br.ui:checkSectionState(section)
+ -------------------------
+ --- Defensive Options ---
+ -------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Defensive")
+ -- Barkskin
+ br.ui:createSpinner(section, "Barkskin", 55, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ -- Natures Vigil
+ br.ui:createDropdownWithout(section, "Nature's Vigil",
+ { "|cff00FF00Always", "|cffFFFF00Cooldowns", "|cffFF0000Never" }, 2,
+ "|cffFFFFFFSet when to use Nature's Vigil")
+ -- Rebirth
+ br.ui:createCheckbox(section, "Rebirth")
+ br.ui:createDropdownWithout(section, "Rebirth - Target", { "|cff00FF00Target", "|cffFF0000Mouseover" }, 1,
+ "|cffFFFFFFTarget to cast on")
+ -- Healing Touch
+ br.ui:createSpinner(section, "Healing Touch", 50, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ br.ui:createDropdownWithout(section, "Healing Touch - OoC", { "|cff00FF00Break Form", "|cffFF0000Keep Form" }, 1,
+ "|cffFFFFFFSelect if Healing Touch is allowed to break shapeshift to heal out of combat.")
+ br.ui:createDropdownWithout(section, "Healing Touch - InC", { "|cff00FF00Immediately", "|cffFF0000Save For DoC" }, 1,
+ "|cffFFFFFFSelect if Predatory Swiftness is used when available or saved for Bloodtalons.")
+ -- Rejuvenation
+ br.ui:createSpinner(section, "Rejuvenation", 50, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ -- Remove Corruption
+ br.ui:createCheckbox(section, "Remove Corruption")
+ br.ui:createDropdownWithout(section, "Remove Corruption - Target",
+ { "|cff00FF00Player", "|cffFFFF00Target", "|cffFF0000Mouseover" }, 1, "|cffFFFFFFTarget to cast on")
+ -- Renewal
+ br.ui:createSpinner(section, "Renewal", 75, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ -- Revive
+ br.ui:createCheckbox(section, "Revive")
+ br.ui:createDropdownWithout(section, "Revive - Target", { "|cff00FF00Target", "|cffFF0000Mouseover" }, 1,
+ "|cffFFFFFFTarget to cast on")
+ -- Soothe
+ br.ui:createCheckbox(section, "Soothe")
+ -- Survival Instincts
+ br.ui:createSpinner(section, "Survival Instincts", 40, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ -- Auto-Heal
+ br.ui:createDropdownWithout(section, "Auto Heal", { "|cffFFDD11LowestHP", "|cffFFDD11Player" }, 1,
+ "|cffFFFFFFSelect Target to Auto-Heal")
+ br.ui:checkSectionState(section)
+ -------------------------
+ --- Interrupt Options ---
+ -------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Interrupts")
+ -- Skull Bash
+ br.ui:createCheckbox(section, "Skull Bash")
+ -- Mighty Bash
+ br.ui:createCheckbox(section, "Mighty Bash")
+ -- Maim
+ br.ui:createCheckbox(section, "Maim")
+ -- Interrupt Percentage
+ br.ui:createSpinnerWithout(section, "Interrupt At", 0, 0, 95, 5,
+ "|cffFFFFFFCast Percent to Cast At (0 is random)")
+ br.ui:checkSectionState(section)
+ --------------------------
+ --- Toggle Key Options ---
+ --------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
+ -- Single/Multi Toggle
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
+ -- Cooldown Key Toggle
+ br.ui:createDropdownWithout(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 3)
+ -- Defensive Key Toggle
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
+ -- Interrupts Key Toggle
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
+ -- Prowl Toggle
+ br.ui:createDropdownWithout(section, "Prowl Mode", br.ui.dropOptions.Toggle, 6)
+ br.ui:checkSectionState(section)
+ end
+ optionTable = { {
+ [1] = "Rotation Options",
+ [2] = rotationOptions,
+ } }
+ return optionTable
+end
+
+--------------
+--- Locals ---
+--------------
+-- BR API Locals
+local actionList = {}
+local buff
+local cast
+local cd
+local charges
+local comboPoints
+local debuff
+local enemies
+local energy
+local equiped
+local module
+local spell
+local talent
+local ui
+local unit
+local units
+local use
+local var
+
+-----------------
+--- Functions ---
+-----------------
+
+-- * Potential Mangle Damage
+-- action.mangle_cat.hit_damage
+local function getMangleDamage()
+ -- Get weapon damage and attack power using Unit API
+ local avgWeaponDmg = unit.weaponDamage("player")
+ local ap = unit.attackPower("player")
+
+ -- Mangle Cat: 515% weapon damage + (AP × 4.2)
+ local baseDmg = (5.15 * avgWeaponDmg) + (ap * 4.2)
+
+ -- Apply multipliers
+ local multiplier = 1.0
+
+ -- Savage Roar: +45% physical damage
+ if buff.savageRoar.exists() then
+ multiplier = multiplier * 1.45
+ end
+
+ -- Tiger's Fury: +15% physical damage
+ if buff.tigersFury.exists() then
+ multiplier = multiplier * 1.15
+ end
+
+ -- Dream of Cenarius: +30% damage
+ if buff.dreamOfCenarius.exists() then
+ multiplier = multiplier * 1.30
+ end
+
+ -- Apply crit as expected value (200% crit damage = 100% extra damage)
+ local critChance = unit.critChance("player") / 100
+ local critMultiplier = 1 + (critChance * 1.0)
+
+ return baseDmg * multiplier * critMultiplier
+end
+
+-- * AutoProwl
+local function autoProwl()
+ if not unit.inCombat() and not buff.prowl.exists() then
+ if #enemies.yards20 > 0 then return true end
+ if #enemies.yards20nc > 0 then
+ for i = 1, #enemies.yards20nc do
+ local thisUnit = enemies.yards20nc[i]
+ local threatRange = math.max((20 + (unit.level(thisUnit) - unit.level())), 5)
+ local react = unit.reaction(thisUnit) or 10
+ if unit.distance(thisUnit) < threatRange and (react < 4 or (unit.isUnit("target", thisUnit) and react == 4)) and unit.enemy(thisUnit) and unit.canAttack(thisUnit) then
+ return true
+ end
+ end
+ end
+ -- if unit.isDummy("target") and unit.distance("target") < 20 then return true end
+ end
+ return false
+end
+-- * Ferocious Bite Finish
+local function ferociousBiteFinish(thisUnit)
+ local desc = br._G.C_Spell.GetSpellDescription(spell.ferociousBite.id())
+ local damage = 0
+ local finishHim = false
+ if ui.value("Ferocious Bite Execute") == 3 or comboPoints(thisUnit) == 0 or unit.isDummy(thisUnit) then return false end
+ local comboStart = desc:find(" " .. comboPoints(thisUnit) .. " ", 1, true)
+ if comboStart ~= nil then
+ comboStart = comboStart + 2
+ local damageList = desc:sub(comboStart, desc:len())
+ comboStart = damageList:find(": ", 1, true) + 2
+ damageList = damageList:sub(comboStart, desc:len())
+ local comboEnd = damageList:find(" ", 1, true) - 1
+ damageList = damageList:sub(1, comboEnd)
+ damage = damageList:gsub(",", "")
+ end
+ local lower = tonumber(string.match(damage, "^(%d+)%-%d+$")) or 0
+ finishHim = tonumber(lower) >= unit.health(thisUnit)
+ return finishHim
+end
+-- * Get Mark Unit Option
+local getMarkUnitOption = function(option)
+ local thisTar = ui.value(option)
+ local thisUnit
+ if thisTar == 1 then
+ thisUnit = "player"
+ end
+ if thisTar == 2 then
+ thisUnit = "target"
+ end
+ if thisTar == 3 then
+ thisUnit = "mouseover"
+ end
+ if thisTar == 4 then
+ thisUnit = "focus"
+ end
+ if thisTar == 5 then
+ -- Group mode: Only buff when:
+ -- 1. At least one member needs the buff
+ -- 2. All current party/raid members are alive and in range
+ -- 3. Group size has been stable for at least 3 seconds (prevents buffing when someone just left)
+
+ -- Don't buff if group size changed recently (within 3 seconds)
+ if not br.engines.healingEngine:isGroupStable(3) then
+ return nil
+ end
+
+ local allAliveAndInRange = true
+ local someoneNeedsBuff = false
+ local unitNeedingBuff = nil
+ local currentGroupSize = #br.engines.healingEngine.friend
+
+ -- If only the player is present in the group, behave like the Player option
+ if currentGroupSize <= 1 then
+ return "player"
+ end
+
+ if currentGroupSize > 1 then
+ -- First pass: Check if all members are alive and in range
+ for i = 1, currentGroupSize do
+ local checkUnit = br.engines.healingEngine.friend[i].unit
+ if unit.deadOrGhost(checkUnit) or unit.distance(checkUnit) >= 40 then
+ allAliveAndInRange = false
+ break
+ end
+ end
+
+ -- Second pass: Check if anyone needs the buff
+ if allAliveAndInRange then
+ for i = 1, currentGroupSize do
+ local nextUnit = br.engines.healingEngine.friend[i].unit
+ if buff.markOfTheWild.refresh(nextUnit) and unit.distance(nextUnit) < 40 then
+ someoneNeedsBuff = true
+ if unitNeedingBuff == nil then
+ unitNeedingBuff = nextUnit
+ end
+ end
+ end
+ end
+ end
+
+ -- Only return a unit if all members are ready AND someone needs the buff
+ if allAliveAndInRange and someoneNeedsBuff then
+ thisUnit = unitNeedingBuff or "player"
+ else
+ return nil
+ end
+ end
+ return thisUnit
+end
+-- * Cast Healing Touch
+local function castHealingTouch(healingTouchUnit, tag)
+ -- Break Form
+ -- if unit.form() ~= 0 and not buff.predatorySwiftness.exists() and unit.isUnit(healingTouchUnit, "player") then
+ -- unit.cancelForm()
+ -- ui.debug("Cancel Form [Healing Touch - "..tag.."]")
+ -- end
+ -- Lowest Party/Raid or Player
+ if unit.form() == 0 or buff.predatorySwiftness.exists() or (buff.naturesSwiftness.exists() and unit.isUnit(healingTouchUnit, "player")) then
+ if cast.healingTouch(healingTouchUnit) then
+ ui.debug("Casting Healing Touch ["..tag.."] on " .. unit.name(healingTouchUnit))
+ return true
+ end
+ end
+end
+
+--------------------
+--- Action Lists ---
+--------------------
+
+-- * Action List - Extras
+actionList.Extras = function()
+ -- * Shapeshift Form Management
+ if ui.checked("Auto Shapeshifts") then --and br.debug.timer:useTimer("debugShapeshift", 0.25) then
+ -- Flight Form
+ if cast.able.travelForm("player") and not unit.inCombat() and unit.canFly() and not unit.swimming() and unit.fallDist() > 90
+ --[[falling > ui.value("Fall Timer")]] and unit.level() >= 24 and not buff.prowl.exists()
+ then
+ if unit.form() ~= 0 and not cast.last.travelForm() then
+ unit.cancelForm()
+ ui.debug("Cancel Form [Flying]")
+ elseif unit.form() == 0 then
+ if cast.travelForm("player") then
+ ui.debug("Casting Travel Form [Flying]")
+ return true
+ end
+ end
+ end
+ -- * Aquatic Form
+ if cast.able.aquaticForm("player") and (not unit.inCombat() --[[or unit.distance("target") >= 10--]])
+ and unit.swimming() and not buff.aquaticForm.exists() and not buff.prowl.exists() and unit.moving()
+ then
+ if unit.form() ~= 0 and not cast.last.aquaticForm() then
+ unit.cancelForm()
+ ui.debug("Cancel Form [Swimming]")
+ elseif unit.form() == 0 then
+ if cast.aquaticForm("player") then
+ ui.debug("Casting Aquatic Form [Swimming]")
+ return true
+ end
+ end
+ end
+ -- * Cat Form
+ if cast.able.catForm() and not buff.catForm.exists() and not unit.mounted() and not unit.flying() then
+ -- Cat Form when not swimming or flying or stag and not in combat
+ if unit.moving() and not unit.swimming() and not unit.flying()
+ and not buff.travelForm.exists() and not (buff.flightForm.exists() or buff.swiftFlightForm.exists())
+ then
+ if cast.catForm("player") then
+ ui.debug("Casting Cat Form [No Swim / Travel / Combat]")
+ return true
+ end
+ end
+ -- Cat Form when not in combat and target selected and within 20yrds
+ if not unit.inCombat() and unit.valid("target") and ((unit.distance("target") < 30 and not unit.swimming())
+ or (unit.distance("target") < 10 and unit.swimming()))
+ then
+ if cast.catForm("player") then
+ ui.debug("Casting Cat Form [Target In 20yrds]")
+ return true
+ end
+ end
+ -- Cat Form - Less Fall Damage
+ if (not unit.canFly() or unit.inCombat() or unit.level() < 24 or not unit.outdoors())
+ and (not unit.swimming() or (not unit.moving() and unit.swimming() and #enemies.yards5f > 0))
+ and unit.fallDist() > 90 --falling > ui.value("Fall Timer")
+ then
+ if cast.catForm("player") then
+ ui.debug("Casting Cat Form [Reduce Fall Damage]")
+ return true
+ end
+ end
+ end
+ end -- End Shapeshift Form Management
+ -- * Perma Fire Cat
+ if ui.checked("Perma Fire Cat") and (use.able.fandralsSeedPouch() or use.able.burningSeeds())
+ and not unit.inCombat() and not buff.prowl.exists() and buff.catForm.exists()
+ then
+ if not buff.burningEssence.exists() then
+ -- Fandral's Seed Pouch
+ if use.able.fandralsSeedPouch() and equiped.fandralsSeedPouch() then
+ if use.fandralsSeedPouch() then
+ ui.debug("Using Fandral's Seed Pouch")
+ return true
+ end
+ -- Burning Seeds
+ elseif use.able.burningSeeds() then
+ if use.burningSeeds() then
+ ui.debug("Using Burning Seeds")
+ return true
+ end
+ end
+ end
+ end -- End Perma Fire Cat
+ -- * Death Cat mode
+ if ui.checked("Death Cat Mode") and buff.catForm.exists() then
+ if unit.exists("target") and unit.distance(units.dyn8AOE) > 8 then
+ unit.clearTarget()
+ end
+ if autoProwl() then
+ -- *Tiger's Fury - Low Energy
+ if ui.checked("Tiger's Fury") and cast.able.tigersFury() and energy.deficit() > 60 then
+ if cast.tigersFury() then
+ ui.debug("Casting Tiger's Fury [Death Cat Mode]")
+ return true
+ end
+ end
+ -- * Savage Roar - Use Combo Points
+ if cast.able.savageRoar() and comboPoints(units.dyn5) >= 5 then
+ if cast.savageRoar() then
+ ui.debug("Casting Savage Roar [Death Cat Mode]")
+ return true
+ end
+ end
+ -- * Shred - Single
+ if cast.able.shred() and #enemies.yards5f == 1 and not unit.facing(units.dyn5,"player") then
+ if cast.shred() then
+ ui.debug("Casting Shred [Death Cat Mode]"); var.swipeSoon = nil; return true
+ end
+ end
+ -- * Swipe - AoE
+ if cast.able.swipe("player", "aoe", 1, 8) and #enemies.yards8 > 1 then
+ if var.swipeSoon == nil then
+ var.swipeSoon = var.getTime;
+ end
+ if var.swipeSoon ~= nil and var.swipeSoon < var.getTime - 1 then
+ if cast.swipe("player", "aoe", 1, 8) then
+ ui.debug("Casting Swipe [Death Cat Mode]"); var.swipeSoon = nil; return true
+ end
+ end
+ end
+ end -- End 20yrd Enemy Scan
+ end -- End Death Cat Mode
+ -- -- * Mark of the Wild
+ if ui.checked("Mark of the Wild") and not (buff.legacyOfTheEmperor.exists() or buff.blessingOfKings.exists()) then
+ var.markUnit = getMarkUnitOption("Mark of the Wild")
+ -- Only cast if we have a valid unit (will be nil in Group mode if not all members are ready)
+ if var.markUnit and cast.able.markOfTheWild(var.markUnit) and buff.markOfTheWild.refresh(var.markUnit)
+ and not unit.inCombat()
+ and not (buff.prowl.exists() or buff.shadowmeld.exists())
+ and not unit.resting() and unit.distance(var.markUnit) < 40
+ then
+ if cast.markOfTheWild(var.markUnit) then
+ ui.debug("Casting Mark of the Wild on " .. unit.name(var.markUnit))
+ return true
+ end
+ end
+ end
+ -- * Dummy Test
+ if ui.checked("DPS Testing") then
+ if unit.exists("target") then
+ if unit.combatTime() >= (tonumber(ui.value("DPS Testing")) * 60) and unit.isDummy() then
+ unit.stopAttack()
+ unit.clearTarget()
+ ui.print(tonumber(ui.value("DPS Testing")) .. " Minute Dummy Test Concluded - Profile Stopped")
+ var.profileStop = true
+ end
+ end
+ end -- End Dummy Test
+end -- End Action List - Extras
+
+-- * Action List - Defensive
+actionList.Defensive = function()
+ if ui.useDefensive() and not unit.mounted() and not (buff.prowl.exists() or buff.shadowmeld.exists()) and not buff.flightForm.exists() and not buff.prowl.exists() then
+ local opValue
+ local thisUnit
+ -- * Rebirth
+ if ui.checked("Rebirth") and unit.inCombat() then
+ opValue = ui.value("Rebirth - Target")
+ if opValue == 1 then
+ thisUnit = "target"
+ elseif opValue == 2 then
+ thisUnit = "mouseover"
+ end
+ if cast.able.rebirth(thisUnit, "dead") and unit.deadOrGhost(thisUnit)
+ and (unit.friend(thisUnit) or unit.player(thisUnit))
+ then
+ if unit.form() ~= 0 then
+ unit.cancelForm()
+ ui.debug("Cancel Form [Rebirth]")
+ elseif unit.form() == 0 then
+ if cast.rebirth(thisUnit, "dead") then
+ ui.debug("Casting Rebirth on " .. unit.name(thisUnit))
+ return true
+ end
+ end
+ end
+ end
+ -- * Revive
+ if ui.checked("Revive") and not unit.inCombat() then
+ opValue = ui.value("Revive - Target")
+ if opValue == 1 then
+ thisUnit = "target"
+ elseif opValue == 2 then
+ thisUnit = "mouseover"
+ end
+ if cast.able.revive(thisUnit, "dead") and unit.deadOrGhost(thisUnit)
+ and (unit.friend(thisUnit) and unit.player(thisUnit))
+ then
+ if cast.revive(thisUnit, "dead") then
+ ui.debug("Casting Revive on " .. unit.name(thisUnit))
+ return true
+ end
+ end
+ end
+ -- * Remove Corruption
+ if ui.checked("Remove Corruption") then
+ opValue = ui.value("Remove Corruption - Target")
+ if opValue == 1 then
+ thisUnit = "player"
+ elseif opValue == 2 then
+ thisUnit = "target"
+ elseif opValue == 3 then
+ thisUnit = "mouseover"
+ end
+ if cast.able.removeCorruption(thisUnit) and (unit.friend(thisUnit) or unit.player(thisUnit))
+ and cast.dispel.removeCorruption(thisUnit)
+ then
+ if cast.removeCorruption(thisUnit) then
+ ui.debug("Casting Remove Corruption on " .. unit.name(thisUnit))
+ return true
+ end
+ end
+ end
+ -- * Soothe
+ if ui.checked("Soothe") then
+ for i = 1, #enemies.yards40 do
+ local sootheUnit = enemies.yards40[i]
+ if cast.able.soothe(sootheUnit) and cast.dispel.soothe(sootheUnit) then
+ if cast.soothe(sootheUnit) then
+ ui.debug("Casting Soothe on " .. unit.name(sootheUnit))
+ return true
+ end
+ end
+ end
+ end
+ -- * Renewal
+ if ui.checked("Renewal") and talent.renewal and unit.inCombat() and cast.able.renewal() and unit.hp() <= ui.value("Renewal") then
+ if cast.renewal() then
+ ui.debug("Casting Renewal")
+ return true
+ end
+ end
+ -- * PowerShift - Breaks Crowd Control (R.I.P Powershifting)
+ if ui.checked("Break Crowd Control") and cast.able.catForm() and cast.noControl.catForm() then
+ if cast.macro("/cast !Cat Form") then
+ ui.debug("Casting Cat Form [Breaking CC]")
+ return true
+ end
+ -- if not cast.noControl.catForm() and var.lastForm ~= 0 then
+ -- cast.form(var.lastForm)
+ -- var.lastForm = 0
+ -- -- if currentForm == var.lastForm or currentForm == 0 then
+ -- -- var.lastForm = 0
+ -- -- end
+ -- elseif cast.noControl.catForm() then
+ -- if unit.form() == 0 then
+ -- cast.catForm("player")
+ -- ui.debug("Casting Cat Form [Breaking CC]")
+ -- else
+ -- for i = 1, unit.formCount() do
+ -- if i == unit.form() then
+ -- var.lastForm = i
+ -- cast.form(i)
+ -- ui.debug("Casting Last Form [Breaking CC]")
+ -- return true
+ -- end
+ -- end
+ -- end
+ -- end
+ end
+ -- * Rejuvenation
+ if ui.checked("Rejuvenation") and not (unit.mounted() or unit.flying())
+ and (ui.value("Auto Heal") ~= 1 or (ui.value("Auto Heal") == 1 and unit.distance(br.engines.healingEngine.friend[1].unit) < 40))
+ then
+ local thisHP = unit.hp()
+ local rejuvUnit = "player"
+ local lowestUnit = unit.lowest(40)
+ local fhp = unit.hp(lowestUnit)
+ if ui.value("Auto Heal") == 1 then
+ thisHP = fhp; rejuvUnit = lowestUnit
+ end
+ local rejuvPercent = ui.value("Rejuvenation")
+ if cast.able.rejuvenation(rejuvUnit) and buff.rejuvenation.refresh(rejuvUnit) and thisHP <= rejuvPercent then
+ if unit.form() ~= 0 and unit.level() < 26 then
+ unit.cancelForm()
+ ui.debug("Cancel Form [Rejuvenation]")
+ elseif unit.form() == 0 then
+ if cast.rejuvenation(rejuvUnit) then
+ ui.debug("Casting Rejuvenation on " .. unit.name(rejuvUnit))
+ return true
+ end
+ end
+ end
+ end
+ -- * Healing Touch
+ if ui.checked("Healing Touch") and cast.able.healingTouch("player")
+ and not (unit.mounted() or unit.flying()) and not cast.current.healingTouch()
+ then
+ local thisHP = unit.hp()
+ local healingTouchUnit = "player"
+ local lowestUnit = unit.lowest(40)
+ local fhp = unit.hp(lowestUnit)
+ if ui.value("Auto Heal") == 1 and unit.distance(lowestUnit) < 40 then
+ thisHP = fhp; healingTouchUnit = lowestUnit
+ else
+ healingTouchUnit = "player"
+ end
+ if not unit.inCombat() then
+ -- Out of Combat: Only heal player unless mouseover is available
+ local oocHealUnit = "player"
+ local oocHP = unit.hp("player")
+ if unit.exists("mouseover") and unit.friend("mouseover") and unit.distance("mouseover") < 40 then
+ oocHealUnit = "mouseover"
+ oocHP = unit.hp("mouseover")
+ end
+ if oocHP <= ui.value("Healing Touch") and (not unit.moving() or buff.predatorySwiftness.exists()) then
+ return castHealingTouch(oocHealUnit, "OoC")
+ end
+ elseif unit.inCombat()then
+ -- Always Use Predatory Swiftness when available
+ if ui.value("Healing Touch - InC") == 1 and (thisHP <= ui.value("Healing Touch")
+ or (buff.predatorySwiftness.exists() and not talent.dreamOfCenarius))
+ then
+ return castHealingTouch(healingTouchUnit, "InC Instant")
+ end
+ -- Hold Predatory Swiftness for Dream of Cenarius unless Health is Below Half of Threshold or Predatory Swiftness is about to Expire.
+ if ui.value("Healing Touch - InC") == 2
+ and (thisHP <= ui.value("Healing Touch") and not buff.predatorySwiftness.exists())
+ or (buff.predatorySwiftness.exists() and buff.predatorySwiftness.remain() < unit.gcd(true) * 2)
+ then
+ return castHealingTouch(healingTouchUnit, "InC DoC Hold")
+ end
+ -- Nature's Swiftness - Emergency Heal
+ if cast.able.naturesSwiftness() and unit.hp() <= 20
+ and not buff.naturesSwiftness.exists() and not buff.predatorySwiftness.exists()
+ then
+ if cast.naturesSwiftness() then
+ ui.debug("Casting Nature's Swiftness [Emergency Heal]")
+ return castHealingTouch(healingTouchUnit, "Emergency Heal")
+ end
+ end
+ end
+ end
+ -- -- * Barkskin
+ if ui.checked("Barkskin") and unit.inCombat() and cast.able.barkskin() and unit.hp() <= ui.value("Barkskin") then
+ if cast.barkskin() then
+ ui.debug("Casting Barkskin")
+ return true
+ end
+ end
+ -- -- * Survival Instincts
+ if ui.checked("Survival Instincts") and unit.inCombat() and cast.able.survivalInstincts()
+ and unit.hp() <= ui.value("Survival Instincts")
+ and not buff.survivalInstincts.exists() --and charges.survivalInstincts.count() > 0
+ then
+ if cast.survivalInstincts() then
+ ui.debug("Casting Survival Instincts")
+ return true
+ end
+ end
+ end -- End Defensive Toggle
+end -- End Action List - Defensive
+
+-- * Action List - Interrupts
+actionList.Interrupts = function()
+ if ui.useInterrupt() and ui.delay("Interrupts", unit.gcd(true))
+ and not (buff.prowl.exists() or buff.shadowmeld.exists())
+ then
+ local thisUnit
+ -- * Skull Bash
+ if ui.checked("Skull Bash") then
+ for i = 1, #enemies.yards13f do
+ thisUnit = enemies.yards13f[i]
+ if cast.able.skullBash(thisUnit) and unit.interruptable(thisUnit, ui.value("Interrupt At")) then
+ if cast.skullBash(thisUnit) then
+ ui.debug("Casting Skull Bash on " .. unit.name(thisUnit))
+ return true
+ end
+ end
+ end
+ end
+ -- * Mighty Bash
+ if ui.checked("Mighty Bash") and talent.mightyBash then
+ for i = 1, #enemies.yards5f do
+ thisUnit = enemies.yards5f[i]
+ if cast.able.mightyBash(thisUnit) and unit.interruptable(thisUnit, ui.value("Interrupt At")) then
+ if cast.mightyBash(thisUnit) then
+ ui.debug("Casting Mighty Bash on " .. unit.name(thisUnit))
+ return true
+ end
+ end
+ end
+ end
+ -- * Maim
+ if ui.checked("Maim") then
+ for i = 1, #enemies.yards5f do
+ thisUnit = enemies.yards5f[i]
+ if cast.able.maim(thisUnit) and unit.interruptable(thisUnit, ui.value("Interrupt At"))
+ and comboPoints(thisUnit) > 0
+ then
+ if cast.maim(thisUnit) then
+ ui.debug("Casting Maim on " .. unit.name(thisUnit))
+ return true
+ end
+ end
+ end
+ end
+ end -- End useInterrupts check
+end -- End Action List - Interrupts
+
+-- * Action List - Cooldown
+actionList.Cooldown = function()
+ if unit.distance(units.dyn5) < 5 and not buff.prowl.exists()then
+ -- * Module- Basic Trinkets
+ -- use_items
+ module.BasicTrinkets()
+ end -- End useCooldowns check
+end -- End Action List - Cooldowns
+
+-- * Action List - Opener (Initial Engagement - Stealth or Non-Stealth)
+actionList.Opener = function(thisUnit, isAoE)
+ -- Use provided unit or default to units.dyn5
+ thisUnit = thisUnit or units.dyn5
+
+ -- Only run if target exists and is valid
+ if not unit.valid(thisUnit) then
+ return false
+ end
+
+ -- If opener has already been cast AND we're in combat, skip
+ -- This allows the opener to retry if we're still out of combat
+ if var.openerCast and unit.inCombat() then
+ return false
+ end
+
+ -- Don't fire opener while falling/moving from Wild Charge
+ -- Wait at least 0.5 seconds after Wild Charge to ensure we're positioned behind target
+ if unit.falling() or (cast.last.wildCharge(1) and cast.timeSinceLast.wildCharge() < 0.5) then
+ return false
+ end
+
+ -- Apply Savage Roar first if missing (can be cast from stealth without breaking it)
+ if not buff.savageRoar.exists() and cast.able.savageRoar("player") then
+ if cast.savageRoar("player") then
+ ui.debug("Casting Savage Roar [Opener - Pre-buff]")
+ return true -- Applied, will try opener again next frame
+ end
+ end
+
+ local targetDistance = unit.distance(thisUnit)
+ local behindTarget = not unit.facing(thisUnit, "player")
+ local inStealth = buff.prowl.exists() or buff.shadowmeld.exists()
+
+ -- ==================== RANGE CHECK & WILD CHARGE ====================
+ -- If out of melee range and Wild Charge is available, use it first
+ if ui.checked("Wild Charge") and targetDistance >= 8 and targetDistance <= 25
+ and cast.able.wildCharge(thisUnit)
+ then
+ if cast.wildCharge(thisUnit) then
+ ui.debug("Wild Charge to engage " .. unit.name(thisUnit) .. " [Opener]")
+ return true
+ end
+ end -- If still out of melee range and can't Wild Charge, can't engage yet
+ if targetDistance >= 5 then
+ return false
+ end
+
+ -- ==================== STEALTH OPENERS ====================
+ if inStealth then
+ -- ==================== SINGLE TARGET STEALTH OPENER ====================
+
+ -- * Ravage - Only if available, behind target, and worth using DoTs
+ if cast.able.ravage(thisUnit) then
+ if behindTarget then
+ -- Perfect conditions - cast Ravage
+ if cast.ravage(thisUnit) then
+ var.openerCast = true
+ var.openerTime = var.getTime
+ var.openerAttemptTime = nil
+ var.lastTargetFacing = nil -- Reset facing tracker
+ ui.debug("Casting Ravage on " .. unit.name(thisUnit) .. " [Opener - Stealth Behind]")
+ return true
+ end
+ -- Ravage failed to cast (could be range, GCD, etc) - don't fall through yet
+ return false
+ else
+ -- Not behind target - use smart timeout logic
+ if not var.openerAttemptTime then
+ var.openerAttemptTime = var.getTime
+ end
+
+ local openerWaitTime = var.getTime - var.openerAttemptTime
+
+ -- Dynamic timeout based on situation:
+ -- 1. If just used Wild Charge: Give extra time (1.5s) for positioning to settle
+ -- 2. If player is moving: Give more time (1.2s) - might be repositioning
+ -- 3. Base timeout: 1.0s
+ local timeoutDuration = 1.0 -- Base timeout
+
+ if cast.last.wildCharge(2) then
+ timeoutDuration = 1.5 -- Give Wild Charge time to settle
+ end
+
+ -- If player is moving, we might be trying to get behind - give more time
+ if unit.moving("player") then
+ timeoutDuration = math.max(timeoutDuration, 1.2)
+ end
+
+ if openerWaitTime < timeoutDuration then
+ local reason = "positioning"
+ if cast.last.wildCharge(2) then reason = "Wild Charge settling" end
+ if unit.moving("player") then reason = "player repositioning" end
+
+ -- ui.debug("Waiting for Ravage position [" .. reason .. "] (" ..
+ -- string.format("%.1f", openerWaitTime) .. "s/" ..
+ -- string.format("%.1f", timeoutDuration) .. "s)")
+ return true
+ else
+ -- Timeout - fall through to Rake
+ -- ui.debug("Ravage positioning timeout (" .. string.format("%.1f", openerWaitTime) ..
+ -- "s) - using Rake [Opener - Stealth]")
+ end
+ end
+ elseif not cast.able.ravage(thisUnit) and not cast.last.ravage(1) then
+ -- Ravage not available (wrong level, not learned) - but don't spam if we just cast it
+ -- ui.debug("Ravage not available - using Rake [Opener - Stealth]")
+ end
+
+ -- * Rake - Fallback when Ravage not available, positioning timeout, or not worth waiting
+ -- Only cast if DoTs are worth using (fast-dying targets already handled above)
+ if cast.able.rake(thisUnit) and not ((var.skipPlayerDoTs and unit.player(thisUnit)) or spell.ravage.known()) then
+ if cast.rake(thisUnit) then
+ var.openerCast = true
+ var.openerTime = var.getTime
+ var.openerAttemptTime = nil
+ ui.debug("Casting Rake on " .. unit.name(thisUnit) .. " [Opener - Stealth]")
+ return true
+ end
+ end
+ end
+
+ -- ==================== NON-STEALTH OPENERS ====================
+ if not inStealth then
+ -- * AoE Opener (3+ enemies) - Always Thrash first if available, then Swipe
+ if isAoE then
+ -- Thrash from stealth - applies DoT to all nearby enemies (skip if target dies too fast)
+ if cast.able.thrash("player", "aoe", 1, 8) then
+ if cast.thrash("player", "aoe", 1, 8) then
+ var.openerCast = true
+ var.openerTime = var.getTime
+ var.openerAttemptTime = nil
+ ui.debug("Casting Thrash [Opener - AoE Stealth]")
+ return true
+ end
+ end
+ -- Swipe - Always available, good for fast-dying packs or if Thrash not available
+ if cast.able.swipe("player", "aoe", 1, 8) then
+ if cast.swipe("player", "aoe", 1, 8) then
+ var.openerCast = true
+ var.openerTime = var.getTime
+ var.openerAttemptTime = nil
+ ui.debug("Casting Swipe [Opener - AoE Stealth]")
+ return true
+ end
+ end
+ end
+
+ -- * Single Target Non-Stealth Opener
+ -- Fast-dying: Use direct damage instead of DoTs
+ if not isAoE then
+ -- Normal target: Use Rake (best opener for combo point + DoT)
+ if cast.able.rake(thisUnit) and not (var.skipPlayerDoTs and unit.player(thisUnit)) then
+ if cast.rake(thisUnit) then
+ var.openerCast = true
+ var.openerTime = var.getTime
+ ui.debug("Casting Rake on " .. unit.name(thisUnit) .. " [Opener]")
+ return true
+ end
+ end
+ end
+
+ -- * Mangle - Last resort if nothing else is available
+ if cast.able.mangle(thisUnit) then
+ if cast.mangle(thisUnit) then
+ var.openerCast = true
+ var.openerTime = var.getTime
+ ui.debug("Casting Mangle on " .. unit.name(thisUnit) .. " [Opener - Fallback]")
+ return true
+ end
+ end
+
+ -- * Faerie Fire - Apply Weakened Armor Debuff first (ranged, doesn't break stealth)
+ -- Skip on fast-dying targets
+ if cast.able.faerieFire(thisUnit) and debuff.weakenedArmor.stack(thisUnit) < 3
+ and targetDistance < 35
+ then
+ if cast.faerieFire(thisUnit) then
+ ui.debug("Casting Faerie Fire [Opener - Pre-debuff]")
+ return true
+ end
+ end
+ end
+
+ return false
+end -- End Action List - Opener
+
+-- * Action List - PreCombat
+actionList.PreCombat = function()
+ if not unit.inCombat() and not (unit.flying() or unit.mounted()) then
+ if not (buff.prowl.exists() or buff.shadowmeld.exists()) then
+ -- * Flask: Flask of Spring Blossoms
+ -- flask,type=spring_blossoms
+ module.FlaskUp()
+ -- * Food: Sea Mist Rice Noodles
+ -- food,type=sea_mist_rice_noodles
+ -- Food buff check would be handled by module if it exists
+
+ -- * Mark of the Wild
+ -- mark_of_the_wild,if=!aura.str_agi_int.up
+ -- This is handled elsewhere in the rotation via the Mark of the Wild option
+
+ if unit.valid("target") and unit.exists("target") then
+ --------------------
+ -- Raid Boss Pre-Pull Sequence
+ -- Use ui.mode.bossPull (toggle keybind) to trigger the raid boss pre-pull sequence
+ -- Sequence: [Healing Touch (DoC)] → Cat Form → Savage Roar → Potion → Prowl
+ -- Healing Touch only cast if Dream of Cenarius talented
+ --------------------
+ if ui.mode.bossPull == 1 then
+ -- Step 1: Healing Touch for Dream of Cenarius buff (if talented)
+ if talent.dreamOfCenarius and not buff.dreamOfCenarius.exists() and cast.able.healingTouch("player")
+ and not cast.current.healingTouch()
+ then
+ if cast.healingTouch("player") then
+ ui.debug("Casting Healing Touch - Dream of Cenarius [Boss Pre-Pull]")
+ return true
+ end
+ end
+
+ -- Step 2: Cat Form
+ -- Wait for DoC buff if talented, otherwise proceed immediately
+ if (not talent.dreamOfCenarius or buff.dreamOfCenarius.exists())
+ and not buff.catForm.exists() and cast.able.catForm("player")
+ then
+ if cast.catForm("player") then
+ ui.debug("Casting Cat Form [Boss Pre-Pull]")
+ return true
+ end
+ end
+
+ -- Step 3: Savage Roar
+ if (not talent.dreamOfCenarius or buff.dreamOfCenarius.exists())
+ and buff.catForm.exists() and not buff.savageRoar.exists() and cast.able.savageRoar("player")
+ then
+ if cast.savageRoar("player") then
+ ui.debug("Casting Savage Roar [Boss Pre-Pull]")
+ return true
+ end
+ end
+
+ -- Step 4: Combat Potion
+ if (not talent.dreamOfCenarius or buff.dreamOfCenarius.exists())
+ and buff.catForm.exists() and buff.savageRoar.exists()
+ and ui.useCDs() and ui.checked("Use Combat Potion")
+ then
+ module.CombatPotionUp()
+ end
+
+ -- Step 5: Prowl (after all buffs are up)
+ if (not talent.dreamOfCenarius or buff.dreamOfCenarius.exists())
+ and buff.catForm.exists() and buff.savageRoar.exists()
+ and cast.able.prowl("player") and not buff.prowl.exists()
+ and autoProwl() and ui.mode.prowl == 1
+ and (not unit.resting() or unit.isDummy("target"))
+ and not buff.bsInc.exists()
+ then
+ if cast.prowl("player") then
+ ui.debug("Casting Prowl [Boss Pre-Pull]")
+ return true
+ end
+ end
+ end -- End Raid Boss Pre-Pull Sequence
+
+ --------------------
+ -- Normal Pre-Combat (Non-Boss Pull)
+ --------------------
+ -- Only run if Boss Pull mode is OFF
+ if ui.mode.bossPull ~= 1 then
+ -- * Cat Form - Ensure we're in cat form before prowling
+ if cast.able.catForm("player") and not buff.catForm.exists() then
+ if cast.catForm("player") then
+ ui.debug("Casting Cat Form [Pre-Combat]")
+ return true
+ end
+ end
+
+ -- * Prowl (Stealth)
+ if cast.able.prowl("player") and buff.catForm.exists() and autoProwl() and ui.mode.prowl == 1
+ and not buff.prowl.exists() and (not unit.resting() or unit.isDummy("target"))
+ and not buff.bsInc.exists()
+ then
+ if cast.prowl("player") then
+ ui.debug("Casting Prowl [Pre-Combat]")
+ return true
+ end
+ end
+ end
+ end -- End Target Valid Check
+ end -- End No Stealth
+ if unit.valid("target") and unit.exists("target") then
+ -- * Savage Roar - Always cast if missing (works while prowled)
+ -- Skip if Boss Pull sequence already handled it
+ if cast.able.savageRoar("player") and buff.catForm.exists() and not buff.savageRoar.exists()
+ and not (ui.mode.bossPull == 1 and (not talent.dreamOfCenarius or buff.dreamOfCenarius.exists()))
+ then
+ if cast.savageRoar("player") then
+ ui.debug("Casting Savage Roar [Pre-Combat]")
+ return true
+ end
+ end
+
+ -- * Wild Charge - Get into range if needed
+ if ui.checked("Wild Charge") and cast.able.wildCharge("target") and unit.distance("target") >= 8 then
+ if cast.wildCharge("target") then
+ ui.debug("Wild Charge on " .. unit.name("target") .. " [Pre-Combat]")
+ return true
+ end
+ end
+
+ -- * Opener - Handles both stealth and non-stealth engagement
+ -- Auto-detects single target vs AoE based on enemy count
+ if actionList.Opener("target", ui.useAOE(8,5)) then return true end
+ end -- End Target Valid Check
+ end -- End No Combat
+end -- End Action List - PreCombat
+
+-- * Action List - Combat
+actionList.Combat = function()
+ -- * Cat is 4 fyte!
+ if unit.inCombat() and cast.able.catForm("player") and not buff.catForm.exists()
+ and #enemies.yards5f > 0 and not unit.moving() and ui.checked("Auto Shapeshifts")
+ then
+ if cast.catForm("player") then
+ ui.debug("Casting Cat Form [Combat]")
+ return true
+ end
+ elseif (unit.inCombat() or (not unit.inCombat() and unit.valid(units.dyn5))) and buff.catForm.exists() and not var.profileStop
+ and not ui.checked("Death Cat Mode") and unit.exists(units.dyn5) and cd.global.remain() == 0
+ then
+ -- * Wild Charge
+ if ui.checked("Wild Charge") and cast.able.wildCharge(units.dyn5) then --and unit.valid("target") then
+ if cast.wildCharge(units.dyn5) then
+ ui.debug("Casting Wild Charge on " .. unit.name(units.dyn5) .. " [Out of Melee]")
+ return true
+ end
+ end
+ -- * Ferocious Bite
+ -- execute
+ if cast.able.ferociousBite(units.dyn5) and ferociousBiteFinish(units.dyn5) then
+ if cast.ferociousBite(units.dyn5) then
+ if ui.value("Ferocious Bite Execute") == 1 then
+ ui.print("Ferocious Bite Finished! " ..
+ unit.name(units.dyn5) .. " with " .. br.functions.misc:round2(unit.hp(units.dyn5), 0) .. "% health remaining.")
+ else
+ ui.debug("Casting Ferocious Bite on " .. unit.name(units.dyn5) .. " [Execute]")
+ end
+ return true
+ end
+ end
+ -- -- * Prowl
+ -- -- prowl,if=(buff.bs_inc.down|!in_combat)&!buff.prowl.up
+ -- if cast.able.prowl("player") and buff.catForm.exists() and autoProwl()
+ -- and ui.mode.prowl == 1 and not buff.prowl.exists() and not unit.resting()
+ -- and (((not buff.bsInc.exists() or not unit.inCombat()) and not buff.prowl.exists()))
+ -- then
+ -- if cast.prowl("player") then
+ -- ui.debug("Casting Prowl [Combat]")
+ -- return true
+ -- end
+ -- end
+ -- * Call Action List - AoE
+ -- swap_action_list,name=aoe,if=active_enemies>=5
+ if ui.useAOE(8,5) then
+ if actionList.AoE() then return true end
+ end
+ -- * Call Action List - Single Target
+ -- swap_action_list,name=default,if=active_enemies<5
+ if ui.useST(8, 5) then
+ if actionList.SingleTarget() then return true end
+ end
+ end
+end -- End Action List - Combat
+
+-- * Action List - AoE
+actionList.AoE = function()
+ -- * Opener - AoE Mode
+ if actionList.Opener(units.dyn5, true) then return true end
+
+ -- * Block entire rotation until opener has been cast AND landed
+ -- This prevents any abilities from firing before the engagement opener completes
+ -- Also ensure we wait at least 0.3s after opener to confirm it landed
+ if not var.openerCast or (var.openerTime and var.getTime - var.openerTime < 0.3) then
+ return true -- Wait for opener to execute and land
+ end
+
+ -- * Auto Attack
+ -- auto_attack
+ if cast.able.autoAttack(units.dyn5) and unit.distance(units.dyn5) < 5 and not (buff.prowl.exists() or buff.shadowmeld.exists()) then
+ if cast.autoAttack(units.dyn5) then
+ ui.debug("Casting Auto Attack [AoE]")
+ return true
+ end
+ end
+ -- * Faerie Fire
+ -- faerie_fire,cycle_targets=1,if=debuff.weakened_armor.stack<3
+ if not buff.prowl.exists() and not buff.shadowmeld.exists() then
+ for i = 1, #enemies.yards35 do
+ local thisUnit = enemies.yards35[i]
+ if cast.able.faerieFire(thisUnit) then
+ if debuff.weakenedArmor.stack(thisUnit) < 3 then
+ if cast.faerieFire(thisUnit) then
+ ui.debug("Casting Faerie Fire on " .. unit.name(thisUnit) .. " [AoE]")
+ return true
+ end
+ end
+ end
+ end
+ end
+ -- * Savage Roar
+ -- savage_roar,if=buff.savage_roar.down|(buff.savage_roar.remains<3&combo_points>0)
+ if cast.able.savageRoar() then
+ if not buff.savageRoar.exists() or (buff.savageRoar.remains() < 3 and comboPoints(units.dyn5) > 0) then
+ if cast.savageRoar() then
+ ui.debug("Casting Savage Roar - No Exist / Expire Soon [AoE]")
+ return true
+ end
+ end
+ end
+ -- * Use Item Slot - Hands
+ -- use_item,slot=hands,if=buff.tigers_fury.up
+ -- TODO
+
+ -- * Racial
+ -- blood_fury,if=buff.tigers_fury.up
+ -- berserking,if=buff.tigers_fury.up
+ -- arcane_torrent,if=buff.tigers_fury.up
+ if ui.checked("Use Racial") and buff.tigersFury.exists() then
+ if unit.race() == "Orc" or unit.race() == "Troll" or unit.race() == "BloodElf" then
+ if cast.racial() then
+ ui.debug("Casting Racial [Combat]")
+ return true
+ end
+ end
+ end
+ -- * Tiger's Fury
+ -- tigers_fury,if=energy<=35&!buff.omen_of_clarity.react
+ if ui.checked("Tiger's Fury") and cast.able.tigersFury() then
+ if energy() <= 35 and not buff.clearcasting.exists() then
+ if cast.tigersFury() then
+ ui.debug("Casting Tiger's Fury [AoE]")
+ return true
+ end
+ end
+ end
+ -- * Berserk
+ -- berserk,if=buff.tigers_fury.up
+ if ui.alwaysCdAoENever("Berserk/Incarnation",1,#enemies.yards5) and cast.able.berserk() then
+ if buff.tigersFury.exists() then
+ if cast.berserk() then
+ ui.debug("Casting Berserk [AoE]")
+ return true
+ end
+ end
+ end
+ -- * Action List - Cooldown
+ if actionList.Cooldown() then return true end
+ -- * Pool Resource
+ -- pool_resource,for_next=1
+ -- * Thrash
+ -- thrash_cat,if=buff.rune_of_reorigination.up
+ if buff.runeOfReorigination.exists() then
+ if energy() < 50 and not buff.clearcasting.exists() then
+ return true
+ end
+ if cast.able.thrash("player", "aoe", 1, 8) then
+ if cast.thrash("player", "aoe", 1, 8) then
+ ui.debug("Casting Thrash - Rune of Reorigination [AoE]")
+ return true
+ end
+ end
+ end
+ -- * Pool Resource
+ -- pool_resource,wait=0.1,for_next=1
+ -- * Thrash
+ -- thrash_cat,if=dot.thrash_cat.remains<3|(buff.tigers_fury.up&dot.thrash_cat.remains<9)
+ if (debuff.thrash.remains(units.dyn8AOE) < 3 or (buff.tigersFury.exists() and debuff.thrash.remains(units.dyn8AOE) < 9)) then
+ if energy() < 50 and not buff.clearcasting.exists() then
+ return true
+ end
+ if cast.able.thrash("player", "aoe", 1, 8) then
+ if cast.thrash("player", "aoe", 1, 8) then
+ ui.debug("Casting Thrash [AoE]")
+ return true
+ end
+ end
+ end
+ -- * Savage Roar
+ -- savage_roar,if=buff.savage_roar.remains<9&combo_points>=5
+ if cast.able.savageRoar() then
+ if buff.savageRoar.remains() < 9 and comboPoints(units.dyn5) >= 5 then
+ if cast.savageRoar() then
+ ui.debug("Casting Savage Roar - Max Combo [AoE]")
+ return true
+ end
+ end
+ end
+ -- * Rip
+ -- rip,if=combo_points>=5
+ if cast.able.rip(units.dyn5) and not debuff.convert.exists(units.dyn5) and not (var.skipPlayerDoTs and unit.player(units.dyn5)) then
+ if comboPoints(units.dyn5) >= 5 then
+ if cast.rip(units.dyn5) then
+ ui.debug("Casting Rip [AoE]")
+ return true
+ end
+ end
+ end
+ -- * Rake
+ -- rake,cycle_targets=1,if=active_enemies<8&dot.rake.remains<3&target.time_to_die>=15
+ for i = 1, #enemies.yards5f do
+ local thisUnit = enemies.yards5f[i]
+ if cast.able.rake(thisUnit) and not debuff.convert.exists(thisUnit) and not (var.skipPlayerDoTs and unit.player(thisUnit)) then
+ if #enemies.yards5f < 8 and debuff.rake.remains(thisUnit) < 3 and unit.ttd(thisUnit) >= 15 then
+ if cast.rake(thisUnit) then
+ ui.debug("Casting Rake on " .. unit.name(thisUnit) .. " [AoE]")
+ return true
+ end
+ end
+ end
+ end
+ -- * Swipe
+ -- swipe_cat,if=buff.savage_roar.remains<=5
+ if cast.able.swipe("player", "aoe", 1, 8) and buff.savageRoar.remains() <= 5 then
+ if cast.swipe("player", "aoe", 1, 8) then
+ ui.debug("Casting Swipe - Savage Roar [AoE]")
+ return true
+ end
+ end
+ -- swipe_cat,if=buff.tigers_fury.up|buff.berserk.up
+ if cast.able.swipe("player", "aoe", 1, 8) and (buff.tigersFury.exists() or buff.berserk.exists()) then
+ if cast.swipe("player", "aoe", 1, 8) then
+ ui.debug("Casting Swipe - Tiger's Fury/Berserk [AoE]")
+ return true
+ end
+ end
+ -- swipe_cat,if=cooldown.tigers_fury.remains<3
+ if cast.able.swipe("player", "aoe", 1, 8) and cd.tigersFury.remains() < 3 then
+ if cast.swipe("player", "aoe", 1, 8) then
+ ui.debug("Casting Swipe - Tiger's Fury [AoE]")
+ return true
+ end
+ end
+ -- swipe_cat,if=buff.omen_of_clarity.react
+ if cast.able.swipe("player", "aoe", 1, 8) and buff.clearcasting.exists() then
+ if cast.swipe("player", "aoe", 1, 8) then
+ ui.debug("Casting Swipe - Clearcasting [AoE]")
+ return true
+ end
+ end
+ -- swipe_cat,if=energy.time_to_max<=1
+ if cast.able.swipe("player", "aoe", 1, 8) and energy.ttm() <= 1 then
+ if cast.swipe("player", "aoe", 1, 8) then
+ ui.debug("Casting Swipe - Energy Time to Max [AoE]")
+ return true
+ end
+ end
+end -- End Action List - AoE
+
+-- * Action List - Single Target
+actionList.SingleTarget = function()
+ -- * Opener - Must execute first
+ if actionList.Opener(units.dyn5,false) then return true end
+
+ -- * Block entire rotation until opener has been cast AND landed
+ -- This prevents any abilities from firing before the engagement opener completes
+ -- Also ensure we wait at least 0.3s after opener to confirm it landed
+ if not var.openerCast or (var.openerTime and var.getTime - var.openerTime < 0.3) then
+ return true -- Wait for opener to execute and land
+ end
+
+ -- * Auto Attack
+ -- auto_attack,if=!buff.prowl.up&!buff.shadowmeld.up
+ if cast.able.autoAttack(units.dyn5) and unit.distance(units.dyn5) < 5 and not (buff.prowl.exists() or buff.shadowmeld.exists()) then
+ if cast.autoAttack(units.dyn5) then
+ ui.debug("Casting Auto Attack [Single]")
+ return true
+ end
+ end
+ -- * Call Action List - Interrupts
+ -- skull_bash_cat
+ if actionList.Interrupts() then return true end
+ -- * Force of Nature
+ -- force_of_nature,if=charges=3|(buff.rune_of_reorigination.react&buff.rune_of_reorigination.remains<1)|(buff.vicious.react&buff.vicious.remains<1)|target.time_to_die<20
+ if cast.able.forceOfNature(units.dyn40)
+ and ((charges.forceOfNature.count() == 3 and cast.timeSinceLast.forceOfNature() > unit.gcd(true))
+ or (ui.useCDs() and ((buff.runeOfReorigination.exists() and buff.runeOfReorigination.remains() < 1)
+ or (buff.vicious.exists() and buff.vicious.remains() < 1)
+ or unit.ttd(units.dyn40) < 20)))
+ then
+ if cast.forceOfNature(units.dyn40) then
+ ui.debug("Casting Force of Nature on " .. unit.name(units.dyn40) .. " [Single]")
+ return true
+ end
+ end
+ -- * Racial
+ -- blood_fury,if=buff.tigers_fury.up
+ -- berserking,if=buff.tigers_fury.up
+ -- arcane_torrent,if=buff.tigers_fury.up
+ if ui.checked("Use Racial") and buff.tigersFury.exists() then
+ if unit.race() == "Orc" or unit.race() == "Troll" or unit.race() == "BloodElf" then
+ if cast.racial() then
+ ui.debug("Casting Racial [Single]")
+ return true
+ end
+ end
+ end
+ -- * Ravage
+ -- ravage,if=buff.stealthed.up
+ if cast.able.ravage(units.dyn5) and (buff.prowl.exists() or buff.shadowmeld.exists()) and not unit.facing(units.dyn5, "player") then
+ if cast.ravage(units.dyn5) then
+ ui.debug("Casting Ravage [Single]")
+ return true
+ end
+ end
+ -- * Ferocious Bite
+ -- # Keep Rip from falling off during execute range (only relevant when using DoTs).
+ -- ferocious_bite,if=dot.rip.ticking&dot.rip.remains<=3&target.health.pct<=25
+ if cast.able.ferociousBite(units.dyn5) and comboPoints(units.dyn5) > 0
+ and debuff.rip.exists(units.dyn5) and debuff.rip.remains(units.dyn5) <= 3 and unit.hp(units.dyn5) <= 25
+ then
+ if cast.ferociousBite(units.dyn5) then
+ ui.debug("Casting Ferocious Bite - Maintain Rip [Single]"); return true
+ end
+ end
+ -- * Faerie Fire
+ -- faerie_fire,if=debuff.weakened_armor.stack<3
+ if cast.able.faerieFire(units.dyn5) and not (buff.prowl.exists() or buff.shadowmeld.exists())
+ and debuff.weakenedArmor.stack(units.dyn5) < 3
+ then
+ if cast.faerieFire(units.dyn5) then
+ ui.debug("Casting Faerie Fire [Single]")
+ return true
+ end
+ end
+ -- * Nature's Swiftness
+ -- # Use Nature's Swiftness for an instant Healing Touch when no predatory swiftness is available to proc Dream of Cenarius.
+ -- natures_swiftness,if=talent.dream_of_cenarius.enabled&buff.predatory_swiftness.down&buff.dream_of_cenarius.down
+ if cast.able.naturesSwiftness() and not buff.naturesSwiftness.exists() and talent.dreamOfCenarius and not buff.dreamOfCenarius.exists()
+ and not buff.predatorySwiftness.exists() and comboPoints(units.dyn5) >= 4
+ then
+ if cast.naturesSwiftness() then
+ ui.debug("Casting Nature's Swiftness [Single]")
+ return true
+ end
+ end
+ -- * Healing Touch
+ -- # Proc Dream of Cenarius at 4+ CP or when PS is about to expire.
+ -- healing_touch,if=talent.dream_of_cenarius.enabled&buff.predatory_swiftness.up&buff.dream_of_cenarius.down&(buff.predatory_swiftness.remains<1.5|combo_points>=4)
+ if cast.able.healingTouch() and talent.dreamOfCenarius and not buff.dreamOfCenarius.exists()
+ and ((buff.predatorySwiftness.exists() and (buff.predatorySwiftness.remains() < 1.5 or comboPoints(units.dyn5) >= 4))
+ or (buff.naturesSwiftness.exists() and comboPoints(units.dyn5) >= 4))
+ then
+ if cast.healingTouch("player") then
+ ui.debug("Casting Healing Touch [Single]")
+ return true
+ end
+ end
+ -- * Savage Roar
+ -- savage_roar,if=buff.savage_roar.down
+ if cast.able.savageRoar() and not buff.savageRoar.exists() then
+ if cast.savageRoar() then
+ ui.debug("Casting Savage Roar - No Exist [Single]")
+ return true
+ end
+ end
+ -- * Tiger's Fury
+ -- tigers_fury,if=energy<=35&!buff.omen_of_clarity.react
+ if ui.checked("Tiger's Fury") and cast.able.tigersFury() and energy() <= 35 and not buff.clearcasting.exists() then
+ if cast.tigersFury() then
+ ui.debug("Casting Tiger's Fury [Single]")
+ return true
+ end
+ end
+ -- * Berserk
+ -- berserk,if=buff.tigers_fury.up|(target.time_to_die<18&cooldown.tigers_fury.remains>6)
+ if ui.alwaysCdAoENever("Berserk/Incarnation",1,#enemies.yards5) and cast.able.berserk()
+ and (buff.tigersFury.exists() or (unit.ttd(units.dyn5) < 18 and cd.tigersFury.remains() > 6))
+ then
+ if cast.berserk() then
+ ui.debug("Casting Berserk [Single]")
+ return true
+ end
+ end
+ -- * Action List - Cooldown
+ if actionList.Cooldown() then return true end
+ -- * Use Item Slot - Hands
+ -- use_item,slot=hands,if=buff.tigers_fury.up
+ -- TODO
+
+ -- * Thrash
+ -- thrash_cat,if=buff.omen_of_clarity.react&dot.thrash_cat.remains<3&target.time_to_die>=6
+ if cast.able.thrash("player", "aoe", 1, 8) and not ui.mode.rotation == 3 and buff.clearcasting.exists() and debuff.thrash.remains(units.dyn5) < 3 and unit.ttd(units.dyn5) >= 6 then
+ if cast.thrash("player", "aoe", 1, 8) then
+ ui.debug("Casting Thrash [Single]")
+ return true
+ end
+ end
+ -- * Ferocious Bite
+ -- ferocious_bite,if=target.time_to_die<=1&combo_points>=3
+ if cast.able.ferociousBite(units.dyn5)
+ and ((unit.ttd(units.dyn5) <= 1 or ferociousBiteFinish(units.dyn5)) and comboPoints(units.dyn5) >= 3)
+ then
+ if cast.ferociousBite(units.dyn5) then
+ ui.debug("Casting Ferocious Bite - TTD <= 1 [Single]")
+ return true
+ end
+ end
+ -- * Savage Roar
+ -- savage_roar,if=buff.savage_roar.remains<=3&combo_points>0&target.health.pct<25
+ if cast.able.savageRoar() and buff.savageRoar.remains() <= 3 and comboPoints(units.dyn5) > 0 and unit.hp(units.dyn5) < 25 then
+ if cast.savageRoar() then
+ ui.debug("Casting Savage Roar - Low Target HP [Single]")
+ return true
+ end
+ end
+ -- * Potion - Virmen's Bite
+ -- # Potion near or during execute range when Rune is up and we have 5 CP.
+ -- virmens_bite_potion,if=(combo_points>=5&(target.time_to_die*(target.health.pct-25)%target.health.pct)<15&buff.rune_of_reorigination.up)|target.time_to_die<=40
+ if ui.useCDs() and ui.checked("Use Combat Potion") and not (buff.prowl.exists() or buff.shadowmeld.exists())
+ and ((comboPoints(units.dyn5) >= 5 and (unit.ttd(units.dyn5) * (unit.hp(units.dyn5) - 25) / unit.hp(units.dyn5)) < 15
+ and buff.runeOfReorigination.exists()) or unit.ttd(units.dyn5) <= 40)
+ then
+ module.CombatPotionUp()
+ end
+
+ -- * Rip
+ -- # Overwrite Rip if it's at least 15% stronger than the current.
+ -- rip,if=combo_points>=5&action.rip.tick_damage%dot.rip.tick_dmg>=1.15&target.time_to_die>30
+ if cast.able.rip(units.dyn5) and not debuff.convert.exists(units.dyn5) and not (var.skipPlayerDoTs and unit.player(units.dyn5))
+ and comboPoints(units.dyn5) >= 5 and debuff.rip.applied(units.dyn5) > 0 -- ensure an existing Rip to compare
+ and (debuff.rip.calc() / debuff.rip.applied(units.dyn5)) >= 1.15 and unit.ttd(units.dyn5) > 30
+ then
+ if cast.rip(units.dyn5) then
+ ui.debug("Casting Rip - Overwrite [Single]")
+ return true
+ end
+ end
+ -- # Use 4 or more CP to apply Rip if Rune of Reorigination is about to expire and it's at least close to the current rip in damage.
+ -- rip,if=combo_points>=4&action.rip.tick_damage%dot.rip.tick_dmg>=0.95&target.time_to_die>30&buff.rune_of_reorigination.up&buff.rune_of_reorigination.remains<=1.5
+ if cast.able.rip(units.dyn5) and not (var.skipPlayerDoTs and unit.player(units.dyn5)) and comboPoints(units.dyn5) >= 4 and (debuff.rip.calc() / debuff.rip.applied(units.dyn5)) >= 0.95 and unit.ttd(units.dyn5) > 30
+ and buff.runeOfReorigination.exists() and buff.runeOfReorigination.remains() <= 1.5
+ then
+ if cast.rip(units.dyn5) then
+ ui.debug("Casting Rip - Rune of Reorigination [Single]")
+ return true
+ end
+ end
+ -- * Pool Resource
+ -- # Pool 50 energy for Ferocious Bite (only when using DoTs and Rip is active).
+ -- pool_resource,if=combo_points>=5&target.health.pct<=25&dot.rip.ticking&!(energy>=50|(buff.berserk.up&energy>=25))
+ if comboPoints(units.dyn5) >= 5 and unit.hp(units.dyn5) <= 25 and debuff.rip.exists(units.dyn5)
+ and not (energy() >= 50 or (buff.berserk.exists() and energy() >= 25))
+ then
+ -- ui.debug("Pooling 50 energy for Ferocious Bite [Single]")
+ return true
+ end
+ -- * Ferocious Bite
+ -- # Execute range finisher - use on low HP targets regardless of Rip when not using DoTs
+ -- ferocious_bite,if=combo_points>=5&dot.rip.ticking&target.health.pct<=25
+ if cast.able.ferociousBite(units.dyn5) and comboPoints(units.dyn5) >= 5
+ and debuff.rip.exists(units.dyn5) and unit.hp(units.dyn5) <= 25
+ then
+ if cast.ferociousBite(units.dyn5) then
+ ui.debug("Casting Ferocious Bite - Low Target HP [Single]")
+ return true
+ end
+ end
+ -- * Rip
+ -- 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 cast.able.rip(units.dyn5) and not debuff.convert.exists(units.dyn5) and not (var.skipPlayerDoTs and unit.player(units.dyn5))
+ and comboPoints(units.dyn5) >= 5 and unit.ttd(units.dyn5) >= 6 and debuff.rip.remains(units.dyn5) < 2
+ and (buff.berserk.exists() or debuff.rip.remains(units.dyn5) + 1.9 <= cd.tigersFury.remains())
+ then
+ if cast.rip(units.dyn5) then
+ ui.debug("Casting Rip [Single]")
+ return true
+ end
+ end
+ -- * Savage Roar
+ -- savage_roar,if=buff.savage_roar.remains<=3&combo_points>0&buff.savage_roar.remains+2>dot.rip.remains
+ if cast.able.savageRoar() and buff.savageRoar.remains() <= 3 and comboPoints(units.dyn5) > 0
+ and buff.savageRoar.remains() + 2 > debuff.rip.remains(units.dyn5)
+ then
+ if cast.savageRoar() then
+ ui.debug("Casting Savage Roar - Remain <= 3 [Single]")
+ return true
+ end
+ end
+ -- savage_roar,if=buff.savage_roar.remains<=6&combo_points>=5&buff.savage_roar.remains+2<=dot.rip.remains&dot.rip.ticking
+ if cast.able.savageRoar() and buff.savageRoar.remains() <= 6 and comboPoints(units.dyn5) >= 5
+ and buff.savageRoar.remains() + 2 <= debuff.rip.remains(units.dyn5) and debuff.rip.exists(units.dyn5)
+ then
+ if cast.savageRoar() then
+ ui.debug("Casting Savage Roar - Remain <= 6 [Single]")
+ return true
+ end
+ end
+ -- # Savage Roar if we're about to energy cap and it will keep our Rip from expiring around the same time as Savage Roar.
+ -- savage_roar,if=buff.savage_roar.remains<=12&combo_points>=5&energy.time_to_max<=1&buff.savage_roar.remains<=dot.rip.remains+6&dot.rip.ticking
+ if cast.able.savageRoar() and buff.savageRoar.remains() <= 12 and comboPoints(units.dyn5) >= 5 and energy.ttm() <= 1
+ and buff.savageRoar.remains() <= debuff.rip.remains(units.dyn5) + 6 and debuff.rip.exists(units.dyn5)
+ then
+ if cast.savageRoar() then
+ ui.debug("Casting Savage Roar - Energy Cap [Single]")
+ return true
+ end
+ end
+ -- * Rake
+ -- # Refresh Rake as Re-Origination is about to end if Rake has <9 seconds left.
+ -- rake,if=buff.rune_of_reorigination.up&dot.rake.remains<9&buff.rune_of_reorigination.remains<=1.5
+ if cast.able.rake(units.dyn5) and not (var.skipPlayerDoTs and unit.player(units.dyn5))
+ and buff.runeOfReorigination.exists() and debuff.rake.remains(units.dyn5) < 9
+ and buff.runeOfReorigination.remains() <= 1.5
+ then
+ if cast.rake(units.dyn5) then
+ ui.debug("Casting Rake - Rune of Reorigination [Single]")
+ return true
+ end
+ end
+ -- # Rake if we can apply a stronger Rake or if it's about to fall off and clipping the last tick won't waste too much damage.
+ -- rake,cycle_targets=1,if=target.time_to_die-dot.rake.remains>3&(action.rake.tick_damage>dot.rake.tick_dmg|(dot.rake.remains<3&action.rake.tick_damage%dot.rake.tick_dmg>=0.75))
+ if not (buff.prowl.exists() or buff.shadowmeld.exists()) then
+ for i = 1, #enemies.yards5f do
+ local thisUnit = enemies.yards5f[i]
+ local applied = debuff.rake.applied(thisUnit) > 0 and debuff.rake.applied(thisUnit) or 1
+ if cast.able.rake(thisUnit) and not (var.skipPlayerDoTs and unit.player(thisUnit))
+ and (unit.ttd(thisUnit) - debuff.rake.remains(thisUnit) > 3)
+ and (debuff.rake.calc(thisUnit) > applied
+ or (debuff.rake.remains(thisUnit) < 3
+ and (debuff.rake.calc(thisUnit) / applied) >= 0.75))
+ then
+ if cast.rake(thisUnit) then
+ ui.debug("Casting Rake on " .. unit.name(thisUnit) .. " [Single]")
+ return true
+ end
+ end
+ end
+ end
+ -- * Thrash
+ -- thrash_cat,if=target.time_to_die>=6&dot.thrash_cat.remains<3&(dot.rip.remains>=8&buff.savage_roar.remains>=12|buff.berserk.up|combo_points>=5)&dot.rip.ticking
+ if not ui.mode.rotation == 3 and unit.ttd(units.dyn8AOE) >= 6 and debuff.thrash.remains(units.dyn8AOE) < 3
+ and ((debuff.rip.remains(units.dyn8AOE) >= 8 and buff.savageRoar.remains() >= 12)
+ or buff.berserk.exists() or comboPoints(units.dyn8AOE) >= 5) and debuff.rip.exists(units.dyn8AOE)
+ then
+ -- Pool Energy for Thrash
+ -- # Pool energy for and maintain Thrash.
+ -- pool_resource,for_next=1
+ if energy() < 50 then return true end
+ if cast.able.thrash("player", "aoe", 1, 8) then
+ if cast.thrash("player", "aoe", 1, 8) then
+ ui.debug("Casting Thrash - Refresh [Single]")
+ return true
+ end
+ end
+ end
+ -- # Pool energy for and clip Thrash if Rune of Re-Origination is expiring.
+ -- pool_resource,for_next=1
+ -- thrash_cat,if=target.time_to_die>=6&dot.thrash_cat.remains<9&buff.rune_of_reorigination.up&buff.rune_of_reorigination.remains<=1.5&dot.rip.ticking
+ if unit.ttd(units.dyn8AOE) >= 6 and debuff.thrash.remains(units.dyn8AOE) < 9
+ and buff.runeOfReorigination.exists() and buff.runeOfReorigination.remains() <= 1.5
+ and debuff.rip.exists(units.dyn8AOE)
+ then
+ if energy() < 50 then return true end
+ if cast.able.thrash("player", "aoe", 1, 8) then
+ if cast.thrash("player", "aoe", 1, 8) then
+ ui.debug("Casting Thrash - Rune of Reorigination [Single]")
+ return true
+ end
+ end
+ end
+ -- * Pool Energy for Ferocious Bite
+ -- # Pool to near-full energy before casting Ferocious Bite.
+ -- pool_resource,if=combo_points>=5&!(energy.time_to_max<=1|(buff.berserk.up&energy>=25)|(buff.feral_rage.up&buff.feral_rage.remains<=1))&dot.rip.ticking
+ if comboPoints(units.dyn5) >= 5
+ and not (energy.ttm() <= 1 or (buff.berserk.exists() and energy() >= 25) or (buff.feralRage.exists() and buff.feralRage.remains() <= 1))
+ and debuff.rip.exists(units.dyn5)
+ then
+ -- ui.debug("Pooling to near-full energy for Ferocious Bite [Single]")
+ return true
+ end
+ -- * Ferocious Bite
+ -- # Ferocious Bite if we reached near-full energy without spending our CP on something else.
+ -- ferocious_bite,if=combo_points>=5&dot.rip.ticking
+ if cast.able.ferociousBite(units.dyn5) and comboPoints(units.dyn5) >= 5 and debuff.rip.exists(units.dyn5) then
+ if cast.ferociousBite(units.dyn5) then
+ ui.debug("Casting Ferocious Bite - "..comboPoints(units.dyn5).." Combo [Single]")
+ return true
+ end
+ end
+ -- * Call Action List - Filler
+ -- # Conditions under which we should execute a CP generator.
+ -- run_action_list,name=filler,if=buff.omen_of_clarity.react
+ if buff.clearcasting.exists() then
+ if actionList.Filler("Clearcasting") then return true end
+ end
+ -- run_action_list,name=filler,if=buff.feral_fury.react
+ if buff.feralFury.exists() then
+ if actionList.Filler("Feral Fury") then return true end
+ end
+ -- run_action_list,name=filler,if=(combo_points<5&dot.rip.remains<3.0)|(combo_points=0&buff.savage_roar.remains<2)
+ -- When not using DoTs, always check filler when combo points < 5
+ if (comboPoints(units.dyn5) < 5 and debuff.rip.remains(units.dyn5) < 3)
+ or (comboPoints(units.dyn5) == 0 and buff.savageRoar.remains() < 2)
+ then
+ if actionList.Filler("Rip/Savage Roar") then return true end
+ end
+ -- run_action_list,name=filler,if=target.time_to_die<=8.5
+ if unit.ttd(units.dyn5) <= 8.5 and comboPoints(units.dyn5) < 5 and not ferociousBiteFinish(units.dyn5) then
+ if actionList.Filler("TTD") then return true end
+ end
+ -- run_action_list,name=filler,if=buff.tigers_fury.up|buff.berserk.up
+ if (buff.tigersFury.exists() or buff.berserk.exists()) then
+ if actionList.Filler("Tiger/Berserk") then return true end
+ end
+ -- run_action_list,name=filler,if=cooldown.tigers_fury.remains<=3
+ if cd.tigersFury.remains() <= 3 then
+ if actionList.Filler("Tiger Soon") then return true end
+ end
+ -- run_action_list,name=filler,if=energy.time_to_max<=1.0
+ if energy.ttm() <= 1.0 then
+ if actionList.Filler("Energy Max") then return true end
+ end
+end -- End Action List - Single Target
+
+-- * Action List - Filler
+actionList.Filler = function(reason)
+ reason = reason and " - " .. reason or ""
+
+ -- * Ravage
+ -- ravage
+ if cast.able.ravage() and (buff.prowl.exists() or buff.shadowmeld.exists()) and not unit.facing(units.dyn5, "player") then
+ if cast.ravage(units.dyn5) then
+ ui.debug("Casting Ravage [Filler" .. reason .. "]")
+ return true
+ end
+ end
+ -- * Rake
+ -- # Rake if it hits harder than Mangle and we won't apply a weaker bleed to the target.
+ -- rake,if=target.time_to_die-dot.rake.remains>3&action.rake.tick_damage*(dot.rake.ticks_remain+1)-dot.rake.tick_dmg*dot.rake.ticks_remain>action.mangle_cat.hit_damage
+ for i = 1, #enemies.yards5f do
+ local thisUnit = enemies.yards5f[i]
+ if cast.able.rake(thisUnit) and not debuff.convert.exists(thisUnit) and not (var.skipPlayerDoTs and unit.player(thisUnit)) then
+ if unit.ttd(thisUnit) - debuff.rake.remains(thisUnit) > 3
+ and (debuff.rake.calc() * (debuff.rake.ticksRemain(thisUnit) + 1)
+ - debuff.rake.applied(thisUnit) * debuff.rake.ticksRemain(thisUnit)
+ > getMangleDamage())
+ then
+ if cast.rake(thisUnit) then
+ ui.debug("Casting Rake on " .. unit.name(thisUnit) .. " [Filler" .. reason .. "]")
+ return true
+ end
+ end
+ end
+ end
+ -- * Shred
+ -- shred,if=(buff.omen_of_clarity.react|buff.berserk.up|energy.regen>=15)&buff.king_of_the_jungle.down
+ if cast.able.shred(units.dyn5) and not unit.facing(units.dyn5,"player")
+ and (not (buff.prowl.exists() or buff.shadowmeld.exists()) or unit.level() < 54)
+ and (buff.clearcasting.exists() or buff.berserk.exists() or energy.regen() >= 15)
+ and not buff.incarnation.exists()
+ then
+ if buff.clearcasting.exists() then
+ if cast.shred(units.dyn5) then
+ ui.debug("Casting Shred (Clearcasting) on " .. unit.name(units.dyn5) .. " [Filler" .. reason .. "]")
+ return true
+ end
+ end
+ if buff.berserk.exists() then
+ if cast.shred(units.dyn5) then
+ ui.debug("Casting Shred (Berserk) on " .. unit.name(units.dyn5) .. " [Filler" .. reason .. "]")
+ return true
+ end
+ end
+ if energy.regen() >= 15 then
+ if cast.shred(units.dyn5) then
+ ui.debug("Casting Shred (Hi Regen) on " .. unit.name(units.dyn5) .. " [Filler" .. reason .. "]")
+ return true
+ end
+ end
+ -- if cast.shred(units.dyn5) then
+ -- ui.debug("Casting Shred on " .. unit.name(units.dyn5) .. " [Filler" .. reason .. "]")
+ -- return true
+ -- end
+ end
+ -- * Mangle
+ -- mangle_cat,if=buff.king_of_the_jungle.down
+ if cast.able.mangle(units.dyn5) and (not (buff.prowl.exists() or buff.shadowmeld.exists()) or unit.level() < 54)
+ -- and (unit.facing(units.dyn5, "player") or (not unit.facing(units.dyn5,"player")
+ -- and not (buff.clearcasting.exists() or buff.berserk.exists() or energy.regen() >= 15)))
+ and not buff.incarnation.exists()
+ and unit.inCombat() -- Only use Mangle in combat, never for engagement
+ then
+ if cast.mangle(units.dyn5) then
+ ui.debug("Casting Mangle on " .. unit.name(units.dyn5) .. " [Filler" .. reason .. "]")
+ return true
+ end
+ end
+end -- End Action List - Filler
+
+----------------
+--- ROTATION ---
+----------------
+local function runRotation()
+ --------------
+ --- Locals ---
+ --------------
+ -- * Initialize
+ if not br.player.initialized then
+ -- BR API
+ br.player.actionList = actionList
+ buff = br.player.buff
+ cast = br.player.cast
+ cd = br.player.cd
+ charges = br.player.charges
+ comboPoints = br.player.power.comboPoints
+ debuff = br.player.debuff
+ enemies = br.player.enemies
+ energy = br.player.power.energy
+ equiped = br.player.equiped
+ module = br.player.module
+ spell = br.player.spell
+ talent = br.player.talent
+ ui = br.player.ui
+ unit = br.player.unit
+ units = br.player.units
+ use = br.player.use
+ var = br.player.variables
+
+ -- General Variables - Init
+ var.getTime = ui.time()
+ var.lastForm = 0
+ var.leftCombat = var.getTime
+ var.lootDelay = 0
+ var.minCount = 3
+ var.noDoT = false
+ var.profileStop = false
+ var.openerCast = false
+ var.openerTime = nil
+ var.openerAttemptTime = nil
+ var.lastTargetFacing = {}
+ var.stealthBreakNotified = false
+
+ br.player.initialized = true
+ end
+
+ -- * Get Best Unit for Range
+ -- units.get(range, aoe)
+ units.get(40)
+ units.get(8, true)
+ units.get(5)
+
+ -- * Get List of Enemies for Range
+ -- enemies.get(range, from unit, no combat, variable)
+ enemies.get(40) -- makes enemies.yards40
+ enemies.get(35) -- makes enemies.yards35
+ enemies.get(20, "player", true) -- makes enemies.yards20nc
+ enemies.get(20)
+ enemies.get(13, "player", false, true) -- makes enemies.yards13f
+ enemies.get(8) -- makes enemies.yards8
+ enemies.get(5, "player", false, true) -- makes enemies.yards5f
+ enemies.get(5)
+
+ -- * General Vars
+ var.multidot = ui.mode.cleave == 1 and ui.mode.rotation < 3
+ var.getTime = br._G.GetTime()
+ var.lootDelay = ui.checked("Auto Loot") and ui.value("Auto Loot") or 0
+ var.minCount = ui.useCDs() and 1 or 3
+
+ -- Skip DoTs on players only in instances/raids (to prevent DoTs on mind-controlled targets)
+ -- In open world/PvP, allow DoTs on players
+ var.skipPlayerDoTs = br._G.IsInInstance()
+
+ -- Reset opener flag when out of combat or target changes
+ if not unit.inCombat() then
+ if var.profileStop then var.profileStop = false end
+ var.leftCombat = var.getTime
+ var.openerCast = false -- Reset opener flag
+ var.openerTime = nil -- Reset opener time
+ var.openerAttemptTime = nil -- Reset opener attempt timer
+ var.lastTargetFacing = {} -- Reset target facing tracker
+ end
+
+ -- Reset stealth break notification when re-entering stealth (independent of combat)
+ local inStealth = buff.prowl.exists() or buff.shadowmeld.exists()
+ if inStealth then
+ var.stealthBreakNotified = false
+ end
+
+ -- Notify when stealth is broken (only once per stealth)
+ if not inStealth and not var.stealthBreakNotified then
+ ui.debug("Stealth Broken")
+ var.stealthBreakNotified = true
+ end
+
+ -- Auto-disable Boss Pull mode when entering combat
+ if unit.inCombat() and ui.mode.bossPull == 1 then
+ ui.setToggle("BossPull", 2) -- Use API function to update toggle
+ ui.debug("Boss Pull mode auto-disabled on combat start")
+ end
+
+ -- Add buff.bsInc.exists()
+ buff.bsInc = buff.bsInc or {}
+ if not buff.bsInc.exists then
+ buff.bsInc.exists = function()
+ return buff.berserk.exists() or buff.incarnation.exists()
+ end
+ end
+ -- Add buff.bsInc.remain()
+ if not buff.bsInc.remains then
+ buff.bsInc.remains = function()
+ return math.max(buff.berserk.remain(), buff.incarnation.remain())
+ end
+ end
+ -- Add cd.bsInc.remains()
+ cd.bsInc = cd.bsInc or {}
+ if not cd.bsInc.remains then
+ cd.bsInc.remains = function()
+ return math.max(cd.berserk.remains(), cd.incarnation.remains())
+ end
+ end
+ -- Add cd.bsInc.duration()
+ cd.bsInc = cd.bsInc or {}
+ if not cd.bsInc.duration then
+ cd.bsInc.duration = function()
+ return math.max(cd.berserk.duration(), cd.incarnation.duration())
+ end
+ end
+
+ -- ui.chatOverlay("Stealthing: "..tostring(buff.prowl.exists() or buff.shadowmeld.exists()))
+
+ ---------------------
+ --- Begin Profile ---
+ ---------------------
+ -- * Profile Stop | Pause
+ if not unit.inCombat() and not unit.exists("target") and var.profileStop then
+ var.profileStop = false
+ elseif (unit.inCombat() and var.profileStop) or ui.pause() then
+ return true
+ else
+ -----------------------
+ --- Extras Rotation ---
+ -----------------------
+ if actionList.Extras() then return true end
+ --------------------------
+ --- Defensive Rotation ---
+ --------------------------
+ if actionList.Defensive() then return true end
+ -- Don't run if rotation mode is off --
+ if ui.mode.rotation ~= 4 then
+ ------------------------------
+ --- Out of Combat Rotation ---
+ ------------------------------
+ if actionList.PreCombat() then return true end
+ --------------------------
+ --- In Combat Rotation ---
+ --------------------------
+ if actionList.Combat() then return true end
+ end
+ end --End Rotation Logic
+end -- End runRotation
+
+local id = 103
+local expansion = br.isMOP
+br.loader.rotations[id] = br.loader.rotations[id] or {}
+br._G.tinsert(br.loader.rotations[id], {
+ name = rotationName,
+ toggles = createToggles,
+ options = createOptions,
+ run = runRotation,
+})
diff --git a/Rotations/Druid/Feral/FeralCuteOne.lua b/Rotations/Druid/Feral/FeralCuteOne.lua
index 887e631876..1655dcaff0 100644
--- a/Rotations/Druid/Feral/FeralCuteOne.lua
+++ b/Rotations/Druid/Feral/FeralCuteOne.lua
@@ -205,17 +205,17 @@ local function createOptions()
--------------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
-- Cooldown Key Toggle
- br.ui:createDropdownWithout(section, "Cooldown Mode", br.dropOptions.Toggle, 3)
+ br.ui:createDropdownWithout(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 3)
-- Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupts Key Toggle
- br.ui:createDropdownWithout(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
-- Cleave Toggle
- br.ui:createDropdownWithout(section, "Cleave Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Cleave Mode", br.ui.dropOptions.Toggle, 6)
-- Prowl Toggle
- br.ui:createDropdownWithout(section, "Prowl Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Prowl Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -349,9 +349,9 @@ local getMarkUnitOption = function(option)
end
if thisTar == 5 then
thisUnit = "player"
- if #br.friend > 1 then
- for i = 1, #br.friend do
- local nextUnit = br.friend[i].unit
+ if #br.engines.healingEngine.friend > 1 then
+ for i = 1, #br.engines.healingEngine.friend do
+ local nextUnit = br.engines.healingEngine.friend[i].unit
if buff.markOfTheWild.refresh(nextUnit) and unit.distance(var.markUnit) < 40 then
thisUnit = nextUnit
break
@@ -371,7 +371,7 @@ actionList.Extras = function()
-- Shapeshift Form Management
if ui.checked("Auto Shapeshifts") then --and br.timer:useTimer("debugShapeshift", 0.25) then
-- Flight Form
- if cast.able.travelForm("player") and not unit.inCombat() and br.canFly() and not unit.swimming() and br.fallDist > 90
+ if cast.able.travelForm("player") and not unit.inCombat() and unit.canFly() and not unit.swimming() and unit.fallDist() > 90
--[[falling > ui.value("Fall Timer")]] and unit.level() >= 24 and not buff.prowl.exists()
then
if unit.form() ~= 0 and not cast.last.travelForm() then
@@ -417,9 +417,9 @@ actionList.Extras = function()
end
end
-- Cat Form - Less Fall Damage
- if (not br.canFly() or unit.inCombat() or unit.level() < 24 or not unit.outdoors())
+ if (not unit.canFly() or unit.inCombat() or unit.level() < 24 or not unit.outdoors())
and (not unit.swimming() or (not unit.moving() and unit.swimming() and #enemies.yards5f > 0))
- and br.fallDist > 90 --falling > ui.value("Fall Timer")
+ and unit.fallDist() > 90 --falling > ui.value("Fall Timer")
then
if cast.catForm("player") then
ui.debug("Casting Cat Form [Reduce Fall Damage]")
@@ -621,7 +621,7 @@ actionList.Defensive = function()
-- Resto Affinity
if talent.restorationAffinity and not (unit.mounted() or unit.flying())
and (ui.value("Auto Heal") ~= 1 or (ui.value("Auto Heal") == 1
- and unit.distance(br.friend[1].unit) < 40))
+ and unit.distance(br.engines.healingEngine.friend[1].unit) < 40))
then
local thisHP = unit.hp()
local thisUnit = "player"
@@ -662,16 +662,16 @@ actionList.Defensive = function()
end
-- Wild Growth
if ui.checked("Wild Growth") and not unit.inCombat() and cast.able.wildGrowth() then
- for i = 1, #br.friend do
- local wildGrowthUnit = br.friend[i].unit
+ for i = 1, #br.engines.healingEngine.friend do
+ local wildGrowthUnit = br.engines.healingEngine.friend[i].unit
local lowHealthCandidates = br.getUnitsToHealAround(wildGrowthUnit, 30, ui.value("Wild Growth"),
- #br.friend)
+ #br.engines.healingEngine.friend)
if #lowHealthCandidates > 1 and not unit.moving() then
if unit.form() ~= 0 then
unit.cancelForm()
ui.debug("Cancel Form [Wild Growth]")
elseif unit.form() == 0 then
- if cast.wildGrowth(br.friend[i].unit) then
+ if cast.wildGrowth(br.engines.healingEngine.friend[i].unit) then
ui.debug("Casting Wild Growth on " .. unit.name(wildGrowthUnit))
return true
end
@@ -1648,7 +1648,7 @@ local function runRotation()
end
var.btGen = var.btGen or {}
var.rakeRefresh = (debuff.rake.refresh(units.dyn5) or debuff.rake.pmultiplier(units.dyn5) < 1.4) and 1 or 0
- var.unit5ID = br.GetObjectID(units.dyn5) or 0
+ var.unit5ID = unit.id(units.dyn5) or 0
var.noDoT = var.unit5ID == 153758 or var.unit5ID == 156857 or var.unit5ID == 156849 or var.unit5ID == 156865 or
var.unit5ID == 156869
-- Add buff.bsInc.exists()
@@ -1680,11 +1680,11 @@ local function runRotation()
end
-- Friends In Range
- var.solo = #br.friend < 2
+ var.solo = #br.engines.healingEngine.friend < 2
var.friendsInRange = false
if not var.solo then
- for i = 1, #br.friend do
- if unit.distance(br.friend[i].unit) < 15 then
+ for i = 1, #br.engines.healingEngine.friend do
+ if unit.distance(br.engines.healingEngine.friend[i].unit) < 15 then
var.friendsInRange = true
break
end
@@ -1860,10 +1860,11 @@ local function runRotation()
end -- End runRotation
local id = 103
-br.rotations[id] = br.rotations[id] or {}
-br._G.tinsert(br.rotations[id], {
+local expansion = br.isRetail
+br.loader.rotations[id] = br.loader.rotations[id] or {}
+br._G.tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
run = runRotation,
-})
+})
\ No newline at end of file
diff --git a/Rotations/Druid/Initial/InitialDruid-MOP.lua b/Rotations/Druid/Initial/InitialDruid-MOP.lua
new file mode 100644
index 0000000000..e8d7bb1568
--- /dev/null
+++ b/Rotations/Druid/Initial/InitialDruid-MOP.lua
@@ -0,0 +1,540 @@
+local rotationName = "Initial Druid" -- Change to name of profile listed in options drop down
+
+---------------
+--- Toggles ---
+---------------
+local function createToggles() -- Define custom toggles
+ -- Rotation Button
+ local RotationModes = {
+ [1] = { mode = "Auto", value = 1, overlay = "Automatic Rotation", tip = "Swaps between Single and Multiple based on number of #enemies.yards8 in range.", highlight = 1, icon = br.player.spells.wrath },
+ [2] = { mode = "Off", value = 2, overlay = "DPS Rotation Disabled", tip = "Disable DPS Rotation", highlight = 0, icon = br.player.spells.rejuvenation }
+ };
+ br.ui:createToggle(RotationModes, "Rotation", 1, 0)
+ -- Defensive Button
+ local DefensiveModes = {
+ [1] = { mode = "On", value = 1, overlay = "Defensive Enabled", tip = "Includes Defensive Cooldowns.", highlight = 1, icon = br.player.spells.rejuvenation },
+ [2] = { mode = "Off", value = 2, overlay = "Defensive Disabled", tip = "No Defensives will be used.", highlight = 0, icon = br.player.spells.rejuvenation }
+ };
+ br.ui:createToggle(DefensiveModes, "Defensive", 2, 0)
+ -- Cooldown Button
+ local CooldownModes = {
+ [1] = { mode = "On", value = 1, overlay = "Cooldown Enabled", tip = "Includes Offensive Cooldowns.", highlight = 1, icon = br.player.spells.berserk },
+ [2] = { mode = "Off", value = 2, overlay = "Cooldown Disabled", tip = "No Offensive Cooldowns Used.", highlight = 0, icon = br.player.spells.berserk },
+ };
+ br.ui:createToggle(CooldownModes, "Cooldown", 3, 0)
+ -- Form Button
+ local FormModes = {
+ [1] = { mode = "Caster", value = 1, overlay = "Caster Form", tip = "Will force and use Caster Form", highlight = 1, icon = br.player.spells.moonkinForm },
+ [2] = { mode = "Cat", value = 2, overlay = "Cat Form", tip = "Will force and use Cat Form", highlight = 0, icon = br.player.spells.catForm },
+ [3] = { mode = "Bear", value = 3, overlay = "Bear Form", tip = "Will force and use Bear Form", highlight = 0, icon = br.player.spells.bearForm }
+ };
+ br.ui:createToggle(FormModes, "Forms", 4, 0)
+ -- Prowl Button
+ local ProwlModes = {
+ [1] = { mode = "On", value = 1, overlay = "Prowl Enabled", tip = "Rotation will use Prowl", highlight = 1, icon = br.player.spells.prowl },
+ [2] = { mode = "Off", value = 2, overlay = "Prowl Disabled", tip = "Rotation will not use Prowl", highlight = 0, icon = br.player.spells.prowl }
+ };
+ br.ui:createToggle(ProwlModes, "Prowl", 5, 0)
+end
+
+---------------
+--- OPTIONS ---
+---------------
+local function createOptions()
+ local optionTable
+
+ local function rotationOptions()
+ local section
+ -----------------------
+ --- GENERAL OPTIONS ---
+ -----------------------
+ section = br.ui:createSection(br.ui.window.profile, "General")
+ br.ui:createSpinnerWithout(section, "Shift Wait Time", 2, 0, 5, 1,
+ "|cffFFFFFFTime in seconds the profile will wait while moving to shift. Combat is instant!")
+ -- Mark of the Wild
+ br.ui:createDropdown(section, "Mark of the Wild",
+ { "|cffFFFFFFPlayer", "|cffFFFFFFTarget", "|cffFFFFFFMouseover", "|cffFFFFFFFocus", "|cffFFFFFFGroup" }, 1,
+ "|cffFFFFFFSet how to use Mark of the Wild")
+ br.ui:checkSectionState(section)
+ ------------------------
+ --- COOLDOWN OPTIONS ---
+ ------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Cooldowns")
+ -- Basic Trinkets Module
+ br.player.module.BasicTrinkets(nil, section)
+ br.ui:checkSectionState(section)
+ -------------------------
+ --- DEFENSIVE OPTIONS ---
+ -------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Defensive")
+ -- Basic Healing Module
+ br.player.module.BasicHealing(section)
+ -- Regrowth
+ br.ui:createSpinner(section, "Regrowth", 50, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ -- Rejuvenation
+ br.ui:createSpinner(section, "Rejuvenation", 50, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ br.ui:checkSectionState(section)
+ ----------------------
+ --- TOGGLE OPTIONS ---
+ ----------------------
+ section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
+ -- Single/Multi Toggle
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
+ --Defensive Key Toggle
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
+ -- Cooldown Key Toggle
+ br.ui:createDropdownWithout(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 6)
+ -- Pause Toggle
+ br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
+ br.ui:checkSectionState(section)
+ end
+ optionTable = { {
+ [1] = "Rotation Options",
+ [2] = rotationOptions,
+ } }
+ return optionTable
+end
+
+--------------
+--- Locals ---
+--------------
+-- BR API Locals - Many of these are located from System/API, this is a sample of commonly used ones but no all inclusive
+local buff
+local cast
+local cd
+local comboPoints
+local debuff
+local enemies
+local energy
+local module
+local ui
+local unit
+local units
+local spell
+local var = {}
+-- General Locals - Common Non-BR API Locals used in profiles
+local haltProfile
+local profileStop
+-- Profile Specific Locals - Any custom to profile locals
+local actionList = {}
+local fbMaxEnergy
+local movingTimer
+
+-----------------
+--- Functions --- -- List all profile specific custom functions here
+-----------------
+-- AutoProwl
+local function autoProwl()
+ if not unit.inCombat() and not buff.prowl.exists() then
+ if #enemies.yards20 > 0 then return true end
+ if #enemies.yards20nc > 0 then
+ for i = 1, #enemies.yards20nc do
+ local thisUnit = enemies.yards20nc[i]
+ local threatRange = math.max((20 + (unit.level(thisUnit) - unit.level())), 5)
+ local react = unit.reaction(thisUnit) or 10
+ if unit.distance(thisUnit) < threatRange and (react < 4 or (unit.isUnit("target", thisUnit) and react == 4)) and unit.enemy(thisUnit) and unit.canAttack(thisUnit) then
+ return true
+ end
+ end
+ end
+ -- if unit.isDummy("target") and unit.distance("target") < 20 then return true end
+ end
+ return false
+end
+-- Ferocious Bite Finish
+local function ferociousBiteFinish(thisUnit)
+ local desc = br._G.C_Spell.GetSpellDescription(spell.ferociousBite.id())
+ local damage = 0
+ local finishHim = false
+ if thisUnit == nil then thisUnit = units.dyn5 end
+ if comboPoints > 0 and not unit.isDummy(thisUnit) then
+ local comboStart = desc:find(" " .. comboPoints .. " ", 1, true)
+ if comboStart ~= nil then
+ comboStart = comboStart + 2
+ local damageList = desc:sub(comboStart, desc:len())
+ comboStart = damageList:find(": ", 1, true) + 2
+ damageList = damageList:sub(comboStart, desc:len())
+ local comboEnd = damageList:find(" ", 1, true) - 1
+ damageList = damageList:sub(1, comboEnd)
+ damage = damageList:gsub(",", "")
+ end
+ local lower = tonumber(damage:match("^(%d+)%-%d+$"))
+ finishHim = tonumber(lower) >= unit.health(thisUnit)
+ end
+ return finishHim
+end
+-- Time Moving
+local function timeMoving()
+ if movingTimer == nil then movingTimer = br._G.GetTime() end
+ if not unit.moving() then
+ movingTimer = br._G.GetTime()
+ end
+ return br._G.GetTime() - movingTimer
+end
+
+local getMarkUnitOption = function(option)
+ local thisTar = ui.value(option)
+ local thisUnit
+ if thisTar == 1 then
+ thisUnit = "player"
+ end
+ if thisTar == 2 then
+ thisUnit = "target"
+ end
+ if thisTar == 3 then
+ thisUnit = "mouseover"
+ end
+ if thisTar == 4 then
+ thisUnit = "focus"
+ end
+ if thisTar == 5 then
+ thisUnit = "player"
+ if #br.engines.healingEngine.friend > 1 then
+ for i = 1, #br.engines.healingEngine.friend do
+ local nextUnit = br.engines.healingEngine.friend[i].unit
+ if buff.markOfTheWild.refresh(nextUnit) and unit.distance(var.markUnit) < 40 then
+ thisUnit = nextUnit
+ break
+ end
+ end
+ end
+ end
+ return thisUnit
+end
+
+--------------------
+--- Action Lists ---
+--------------------
+-- Action List - Extra
+actionList.Extra = function()
+ -- Auto Shapeshift
+ if (not buff.travelForm.exists() and unit.moving() and timeMoving() > ui.value("Shift Wait Time")) or unit.inCombat()
+ or (unit.exists("target") and (unit.id("target") == 164577 or unit.id("target") == 166906) and unit.canAttack("player", "target"))
+ then
+ local formValue = ui.mode.forms
+ -- Bear Form
+ if formValue == 3 and unit.level() >= 8 and cast.able.bearForm() and not buff.bearForm.exists() then
+ if cast.bearForm() then
+ ui.debug("Casting Bear Form")
+ return true
+ end
+ end
+ -- Caster Form
+ if (formValue == 1 or (unit.exists("target") and (unit.id("target") == 164577 or unit.id("target") == 166906) and unit.canAttack("player", "target")))
+ and (buff.bearForm.exists() or buff.catForm.exists())
+ then
+ br._G.RunMacroText("/CancelForm")
+ ui.debug("Casting Caster Form")
+ return true
+ end
+ --Cat Form
+ if (formValue == 2 or (formValue == 3 and unit.level() < 8)) and unit.level() >= 5 and cast.able.catForm() and not buff.catForm.exists()
+ and ((unit.id("target") ~= 164577 and unit.id("target") ~= 166906) or not unit.canAttack("player", "target"))
+ then
+ if cast.catForm() then
+ ui.debug("Casting Cat Form")
+ return true
+ end
+ end
+ end
+ -- -- Mark of the Wild
+ -- if ui.checked("Mark of the Wild") then
+ -- var.markUnit = getMarkUnitOption("Mark of the Wild")
+ -- if cast.able.markOfTheWild(var.markUnit) and buff.markOfTheWild.refresh(var.markUnit) and not unit.resting() and unit.distance(var.markUnit) < 40 then
+ -- if cast.markOfTheWild(var.markUnit) then
+ -- ui.debug("Casting Mark of the Wild")
+ -- return true
+ -- end
+ -- end
+ -- end
+end -- End Action List - Extra
+
+-- Action List - Defensive
+actionList.Defensive = function()
+ if ui.useDefensive() then
+ -- Basic Healing Module
+ module.BasicHealing()
+ -- Rejuvenation
+ if ui.checked("Rejuvenation") and not cast.current.rejuvenation() then
+ if cast.able.rejuvenation("target") and unit.friend("target") and unit.hp("target") <= ui.value("Rejuvenation")
+ and br._G.UnitIsPlayer("target") and buff.rejuvenation.refresh("target")
+ then
+ if buff.catForm.exists() or buff.bearForm.exists() then
+ -- CancelShapeshiftForm()
+ br._G.RunMacroText("/CancelForm")
+ else
+ ui.debug("Attempting to cast Rejuvenation on " .. unit.name("target"))
+ if cast.rejuvenation("target") then
+ return true
+ end
+ end
+ end
+ if cast.able.rejuvenation("player") and not unit.friend("target") and unit.hp() <= ui.value("Rejuvenation") and buff.rejuvenation.refresh("player") then
+ if buff.catForm.exists() or buff.bearForm.exists() then
+ -- CancelShapeshiftForm()
+ br._G.RunMacroText("/CancelForm")
+ else
+ if cast.rejuvenation("player") then
+ ui.debug("Casting Rejuvenation on " .. unit.name("player"))
+ return true
+ end
+ end
+ end
+ end
+ -- Regrowth
+ -- if ui.checked("Regrowth") and cast.able.regrowth() and not cast.current.regrowth() and not unit.moving() then
+ -- if unit.friend("target") and unit.hp("target") <= ui.value("Regrowth") and br._G.UnitIsPlayer("target") then
+ -- if buff.catForm.exists() or buff.bearForm.exists() then
+ -- -- CancelShapeshiftForm()
+ -- br._G.RunMacroText("/CancelForm")
+ -- else
+ -- if cast.regrowth("target") then
+ -- ui.debug("Casting Regrowth on " .. unit.name("target"))
+ -- return true
+ -- end
+ -- end
+ -- end
+ -- if not unit.friend("target") and unit.hp() <= ui.value("Regrowth") then
+ -- if buff.catForm.exists() or buff.bearForm.exists() then
+ -- -- CancelShapeshiftForm()
+ -- br._G.RunMacroText("/CancelForm")
+ -- else
+ -- if cast.regrowth("player") then
+ -- ui.debug("Casting Regrowth on " .. unit.name("player"))
+ -- return true
+ -- end
+ -- end
+ -- end
+ -- end
+ end
+end -- End Action List - Defensive
+
+-- Action List - Cooldowns
+actionList.Cooldowns = function()
+ module.BasicTrinkets()
+end -- End Action List - Cooldowns
+
+-- Action List - Pre-Combat
+actionList.PreCombat = function()
+ if not unit.inCombat() and not (unit.flying() or unit.mounted()) then
+ if not (buff.prowl.exists() or buff.shadowmeld.exists()) then
+ -- Prowl
+ if cast.able.prowl("player") and buff.catForm.exists() and autoProwl() and ui.mode.prowl == 1
+ and not buff.prowl.exists() and (not unit.resting() or unit.isDummy("target"))
+ then
+ if cast.prowl("player") then
+ ui.debug("Casting Prowl [Precombat]")
+ return true
+ end
+ end
+ end -- End No Stealth
+ if unit.valid("target") then
+ local thisDistance = unit.distance("target") or 99
+ -- Wrath
+ if not unit.moving() and not (buff.catForm.exists() or buff.bearForm.exists()) and thisDistance < 40 then
+ if cast.able.wrath("target") and (unit.level() < 2 or not cast.last.wrath() or cast.timeSinceLast.wrath() > unit.gcd(true) + 0.5) then
+ if cast.wrath("target") then
+ ui.debug("Casting Wrath [Precombat]")
+ return true
+ end
+ end
+ end
+ if thisDistance < 5 then
+ -- Shred
+ -- if cast.able.shred() and buff.catForm.exists() then
+ -- if cast.shred() then
+ -- ui.debug("Casting Shred [Precombat]")
+ -- return true
+ -- end
+ -- end
+ -- Mangle
+ if cast.able.mangleBear(units.dyn5) and buff.bearForm.exists() then
+ if cast.mangleBear(units.dyn5) then
+ ui.debug("Casting Mangle [Precombat]")
+ return true
+ end
+ end
+ -- Rake
+ if cast.able.rake() and buff.catForm.exists() then
+ if cast.rake() then
+ ui.debug("Casting Rake [Precombat]")
+ return true
+ end
+ end
+ -- Auto Attack
+ if cast.able.autoAttack("target") and unit.exists("target") and unit.distance("target") < 5 then
+ if cast.autoAttack("target") then
+ ui.debug("Casting Auto Attack [Precombat]")
+ return true
+ end
+ end
+ end
+ end
+ end
+end -- End Action List - PreCombat
+
+-- Action List - Combat
+actionList.Combat = function()
+ if unit.inCombat() and unit.valid("target") and cd.global.remain() == 0 then
+ ------------------------
+ --- In Combat - Main ---
+ ------------------------
+ -- Melee in melee range
+ if unit.exists(units.dyn5) and unit.distance(units.dyn5) < 5 then
+ -- Start Attack
+ if cast.able.autoAttack("target") and unit.exists("target") and unit.distance("target") < 5 then
+ if cast.autoAttack("target") then
+ ui.debug("Casting Auto Attack")
+ return true
+ end
+ end
+ -- Call Action List - Cooldowns
+ if actionList.Cooldowns() then return true end
+ -- Bear Form
+ if buff.bearForm.exists() then
+ -- Maul
+ if cast.able.maul(units.dyn5) then
+ if cast.maul(units.dyn5) then
+ ui.debug("Casting Maul")
+ return true
+ end
+ end
+ -- Mangle
+ if cast.able.mangleBear(units.dyn5) then
+ if cast.mangleBear(units.dyn5) then
+ ui.debug("Casting Mangle")
+ return true
+ end
+ end
+ end
+ -- Cat Form
+ if buff.catForm.exists() then
+ -- Ferocious Bite
+ local finish = ferociousBiteFinish(units.dyn5)
+ if cast.able.ferociousBite(units.dyn5) and ((comboPoints == 5 and fbMaxEnergy) or finish) then
+ if finish then
+ if cast.ferociousBite() then
+ ui.debug("Casting Ferocious Bite [Finish]")
+ return true
+ end
+ else
+ if cast.ferociousBite() then
+ ui.debug("Casting Ferocious Bite")
+ return true
+ end
+ end
+ end
+ -- Rake
+ if cast.able.rake() and comboPoints < 5 and debuff.rake.refresh(units.dyn5) then
+ if cast.rake() then
+ ui.debug("Casting Rake")
+ return true
+ end
+ end
+ -- Mangle
+ if cast.able.mangle() and comboPoints < 5 and not debuff.rake.refresh(units.dyn5) then
+ if cast.mangle() then
+ ui.debug("Casting Mangle")
+ return true
+ end
+ end
+ end
+ end
+ -- Caster Form
+ if not (buff.catForm.exists() or buff.bearForm.exists() or buff.travelForm.exists()) then
+ -- Moonfire
+ if cast.able.moonfire(units.dyn40AOE) and (unit.level() < 5 or not buff.catForm.exists()) and debuff.moonfire.refresh(units.dyn40AOE) then
+ if cast.moonfire(units.dyn40AOE) then
+ ui.debug("Casting Moonfire")
+ return true
+ end
+ end
+ -- Wrath
+ if not unit.moving() and cast.able.wrath() and (unit.level() < 2 or (not cast.last.wrath() and cast.timeSinceLast.wrath() > unit.gcd(true) + 0.5) or not debuff.moonfire.refresh(units.dyn40AOE)) then
+ if cast.wrath() then
+ ui.debug("Casting Wrath")
+ return true
+ end
+ end
+ end
+ end -- End In Combat Rotation
+end -- End Action list - Combat
+
+----------------
+--- ROTATION ---
+----------------
+local function runRotation()
+ ---------------------
+ --- Define Locals ---
+ ---------------------
+ -- BR API Locals
+ buff = br.player.buff
+ cast = br.player.cast
+ cd = br.player.cd
+ comboPoints = br.player.power.comboPoints()
+ debuff = br.player.debuff
+ enemies = br.player.enemies
+ energy = br.player.power.energy()
+ module = br.player.module
+ ui = br.player.ui
+ unit = br.player.unit
+ units = br.player.units
+ spell = br.player.spell
+ -- General Locals
+ profileStop = profileStop or false
+ haltProfile = (unit.inCombat() and profileStop) or br.functions.misc:pause() or ui.mode.rotation == 2 or unit.id("target") == 156716
+ -- Units
+ units.get(5) -- Makes a variable called, units.dyn5
+ units.get(40, true) -- Makes a variable called, units.dyn40AOE
+ -- Enemies
+ enemies.get(5) -- Makes a varaible called, enemies.yards5
+ enemies.get(20) -- Makes a varaible called, enemies.yards20
+ enemies.get(20, "player", true) -- makes enemies.yards20nc
+ enemies.get(40) -- Makes a varaible called, enemies.yards40
+
+ -- Profile Specific Locals
+ fbMaxEnergy = energy >= 50
+ if not unit.inCombat() and not unit.exists("target") then
+ if profileStop then profileStop = false end
+ end
+
+ ---------------------
+ --- Begin Profile ---
+ ---------------------
+ -- Profile Stop | Pause
+ if not unit.inCombat() and not unit.exists("target") and profileStop then
+ profileStop = false
+ elseif haltProfile then
+ return true
+ else
+ ---------------------------------
+ --- Out Of Combat - Rotations ---
+ ---------------------------------
+ -----------------------
+ --- Extras Rotation ---
+ -----------------------
+ if actionList.Extra() then return true end
+ -----------------
+ --- Defensive ---
+ -----------------
+ if actionList.Defensive() then return true end
+ ------------------
+ --- Pre-Combat ---
+ ------------------
+ if actionList.PreCombat() then return true end
+ -----------------------------
+ --- In Combat - Rotations ---
+ -----------------------------
+ if actionList.Combat() then return true end
+ end -- Pause
+ return true
+end -- End runRotation
+local id = 1447 -- Change to the spec id profile is for.
+local expansion = br.isMOP -- Change to the expansion the profile is for.
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
+ name = rotationName,
+ toggles = createToggles,
+ options = createOptions,
+ run = runRotation,
+})
diff --git a/Rotations/Druid/Initial/InitialDruid.lua b/Rotations/Druid/Initial/InitialDruid.lua
index f68a9bad94..0e1359aef1 100644
--- a/Rotations/Druid/Initial/InitialDruid.lua
+++ b/Rotations/Druid/Initial/InitialDruid.lua
@@ -18,8 +18,8 @@ local function createToggles() -- Define custom toggles
br.ui:createToggle(DefensiveModes, "Defensive", 2, 0)
-- Cooldown Button
local CooldownModes = {
- [1] = { mode = "On", value = 1, overlay = "Cooldown Enabled", tip = "Includes Offensive Cooldowns.", highlight = 1, icon = br.player.spells.berserk },
- [2] = { mode = "Off", value = 2, overlay = "Cooldown Disabled", tip = "No Offensive Cooldowns Used.", highlight = 0, icon = br.player.spells.berserk },
+ [1] = { mode = "On", value = 1, overlay = "Cooldown Enabled", tip = "Includes Offensive Cooldowns.", highlight = 1, icon = br.player.spells.rebirth },
+ [2] = { mode = "Off", value = 2, overlay = "Cooldown Disabled", tip = "No Offensive Cooldowns Used.", highlight = 0, icon = br.player.spells.rebirth },
};
br.ui:createToggle(CooldownModes, "Cooldown", 3, 0)
-- Form Button
@@ -54,15 +54,12 @@ local function createOptions()
--- COOLDOWN OPTIONS ---
------------------------
section = br.ui:createSection(br.ui.window.profile, "Cooldowns")
- -- Basic Trinkets Module
- br.player.module.BasicTrinkets(nil, section)
+
br.ui:checkSectionState(section)
-------------------------
--- DEFENSIVE OPTIONS ---
-------------------------
section = br.ui:createSection(br.ui.window.profile, "Defensive")
- -- Basic Healing Module
- br.player.module.BasicHealing(section)
-- Regrowth
br.ui:createSpinner(section, "Regrowth", 50, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
br.ui:checkSectionState(section)
@@ -71,13 +68,11 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
--Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Cooldown Key Toggle
- br.ui:createDropdownWithout(section, "Cooldown Mode", br.dropOptions.Toggle, 6)
- -- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -162,9 +157,9 @@ local getMarkUnitOption = function(option)
end
if thisTar == 5 then
thisUnit = "player"
- if #br.friend > 1 then
- for i = 1, #br.friend do
- local nextUnit = br.friend[i].unit
+ if #br.engines.healingEngine.friend > 1 then
+ for i = 1, #br.engines.healingEngine.friend do
+ local nextUnit = br.engines.healingEngine.friend[i].unit
if buff.markOfTheWild.refresh(nextUnit) and unit.distance(var.markUnit) < 40 then
thisUnit = nextUnit
break
@@ -389,7 +384,7 @@ local function runRotation()
spell = br.player.spell
-- General Locals
profileStop = profileStop or false
- haltProfile = (unit.inCombat() and profileStop) or br.pause() or ui.mode.rotation == 2 or unit.id("target") == 156716
+ haltProfile = (unit.inCombat() and profileStop) or ui.pause() or ui.mode.rotation == 2 or unit.id("target") == 156716
-- Units
units.get(5) -- Makes a variable called, units.dyn5
units.get(40, true) -- Makes a variable called, units.dyn40AOE
@@ -435,8 +430,9 @@ local function runRotation()
return true
end -- End runRotation
local id = 1447 -- Change to the spec id profile is for.
-if br.rotations[id] == nil then br.rotations[id] = {} end
-br._G.tinsert(br.rotations[id], {
+local expansion = br.isRetail -- Change to the expansion the profile is for.
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Evoker/Devastation/DevastationCuteOne.lua b/Rotations/Evoker/Devastation/DevastationCuteOne.lua
index a750f3d2c1..2a2d7091bc 100644
--- a/Rotations/Evoker/Devastation/DevastationCuteOne.lua
+++ b/Rotations/Evoker/Devastation/DevastationCuteOne.lua
@@ -219,13 +219,13 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
-- Cooldown Key Toggle
- br.ui:createDropdownWithout(section, "Cooldown Mode", br.dropOptions.Toggle, 3)
+ br.ui:createDropdownWithout(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 3)
-- Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupts Key Toggle
- br.ui:createDropdownWithout(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -274,8 +274,8 @@ custom.getDeepBreathLocation = function(minUnits)
for i = 21, 50 do
local enemies = #br.player.enemies.rect.get(6, i, false)
if enemies >= minUnits then
- local playerX, playerY, playerZ = br.GetObjectPosition("player")
- local playerFacing = br.GetObjectFacing("player")
+ local playerX, playerY, playerZ = br.functions.unit:GetObjectPosition("player")
+ local playerFacing = br.functions.unit:GetObjectFacing("player")
local x = playerX + (i * math.cos(playerFacing))
local y = playerY + (i * math.sin(playerFacing))
return x, y, playerZ
@@ -301,8 +301,8 @@ actionList.Extra = function()
end -- End Dummy Test-- Dummy DPS Test
-- Blessing of the Bronze
if ui.checked("Blessing of the Bronze") and cast.able.blessingOfTheBronze("player") then
- for i = 1, #br.friend do
- local thisUnit = br.friend[i].unit
+ for i = 1, #br.engines.healingEngine.friend do
+ local thisUnit = br.engines.healingEngine.friend[i].unit
if not unit.deadOrGhost(thisUnit) and unit.distance(thisUnit) < 40 and buff.blessingOfTheBronze.remain(thisUnit) < 600 then
if cast.blessingOfTheBronze("player") then
ui.debug("Casting Blessing of the Bronze")
@@ -1130,7 +1130,7 @@ local function runRotation()
var.moveCast = (not unit.moving() or buff.hover.exists())
var.profileStop = false
- var.haltProfile = (unit.inCombat() and var.profileStop) or unit.mounted() or br.pause() or ui.mode.rotation == 4
+ var.haltProfile = (unit.inCombat() and var.profileStop) or unit.mounted() or br.functions.misc:pause() or ui.mode.rotation == 4
-- Units
units.get(25)
units.get(40)
@@ -1246,8 +1246,9 @@ local function runRotation()
end -- Pause
end -- End runRotation
local id = 1467 -- Change to the spec id profile is for.
-if br.rotations[id] == nil then br.rotations[id] = {} end
-br._G.tinsert(br.rotations[id], {
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Evoker/Initial/InitialEvoker.lua b/Rotations/Evoker/Initial/InitialEvoker.lua
index c9f5bcb7de..e7d1dad4e8 100644
--- a/Rotations/Evoker/Initial/InitialEvoker.lua
+++ b/Rotations/Evoker/Initial/InitialEvoker.lua
@@ -169,13 +169,13 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
-- Cooldown Key Toggle
- br.ui:createDropdownWithout(section, "Cooldown Mode", br.dropOptions.Toggle, 3)
+ br.ui:createDropdownWithout(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 3)
-- Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupts Key Toggle
- br.ui:createDropdownWithout(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -235,8 +235,8 @@ actionList.Extra = function()
end -- End Dummy Test-- Dummy DPS Test
-- Blessing of the Bronze
if ui.checked("Blessing of the Bronze") and cast.able.blessingOfTheBronze("player") then
- for i = 1, #br.friend do
- local thisUnit = br.friend[i].unit
+ for i = 1, #br.engines.healingEngine.friend do
+ local thisUnit = br.engines.healingEngine.friend[i].unit
if not unit.deadOrGhost(thisUnit) and unit.distance(thisUnit) < 40 and buff.blessingOfTheBronze.remain(thisUnit) < 600 then
if cast.blessingOfTheBronze("player") then
ui.debug("Casting Blessing of the Bronze")
@@ -313,7 +313,7 @@ actionList.Interrupt = function()
if ui.useInterrupt() then
for i = 1, #enemies.yards8 do
local thisUnit = enemies.yards8[i]
- if br.canInterrupt(thisUnit, ui.value("Interrupt At")) then
+ if br.functions.spell:canInterrupt(thisUnit, ui.value("Interrupt At")) then
-- Tail Swipe
if ui.checked("Tail Swipe") and cast.able.tailSwipe(thisUnit) and unit.distance(thisUnit) < 8 then
if cast.tailSwipe(thisUnit) then
@@ -482,7 +482,7 @@ local function runRotation()
end
var.moveCast = (not unit.moving() or buff.hover.exists())
var.profileStop = false
- var.haltProfile = (unit.inCombat() and var.profileStop) or unit.mounted() or br.pause() or ui.mode.rotation == 4
+ var.haltProfile = (unit.inCombat() and var.profileStop) or unit.mounted() or br.functions.misc:pause() or ui.mode.rotation == 4
-- Fire Breath Stage
if var.stageFireBreath == nil then var.stageFireBreath = 0 end
@@ -547,10 +547,11 @@ local function runRotation()
end -- Pause
end -- End runRotation
local id = 1465 -- Change to the spec id profile is for.
-if br.rotations[id] == nil then
- br.rotations[id] = {}
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then
+ br.loader.rotations[id] = {}
end
-br._G.tinsert(br.rotations[id], {
+br._G.tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Hunter/BeastMastery/BrewBeastMaster.lua b/Rotations/Hunter/BeastMastery/BrewBeastMaster.lua
index 8b94464cdd..ffa85b2fbd 100644
--- a/Rotations/Hunter/BeastMastery/BrewBeastMaster.lua
+++ b/Rotations/Hunter/BeastMastery/BrewBeastMaster.lua
@@ -6,7 +6,7 @@
-- Readiness = Development
-------------------------------------------------------
local rotationName = "BrewBeastMaster" -- Change to name of profile listed in options drop down
-br.loadSupport("PetCuteOne")
+br.loader.cBuilder:loadSupport("PetCuteOne")
---------------
--- Toggles ---
@@ -58,7 +58,7 @@ local function createOptions()
br.ui:checkSectionState(section)
- br.rotations.support["PetCuteOne"].options()
+ br.loader.rotations.support["PetCuteOne"].options()
------------------------
--- COOLDOWN OPTIONS --- -- Define Cooldown Options
------------------------
@@ -83,15 +83,15 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdown(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdown(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
--Cooldown Key Toggle
- br.ui:createDropdown(section, "Cooldown Mode", br.dropOptions.Toggle, 3)
+ br.ui:createDropdown(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 3)
--Defensive Key Toggle
- br.ui:createDropdown(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupts Key Toggle
- br.ui:createDropdown(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
-- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -141,7 +141,7 @@ actionList.PreCombat = function()
-- Start Attack
if unit.distance("target") <= 40 then
br._G.PetAttack("target")
- if not br._G.C_Spell.IsAutoRepeatSpell(br._G.GetSpellInfo(75)) and unit.exists("target") then
+ if not br._G.C_Spell.IsAutoRepeatSpell(br.api.wow.GetSpellInfo(75)) and unit.exists("target") then
br._G.StartAttack("target")
end
end
@@ -156,7 +156,7 @@ local function runRotation() -- This is the main profile loop, any below this po
if math.random() > 0.80 then return false end;
if actionList.PetManagement == nil then
- actionList.PetManagement = br.rotations.support["PetCuteOne"].run
+ actionList.PetManagement = br.loader.rotations.support["PetCuteOne"].run
end
---------------------
@@ -217,7 +217,7 @@ local function runRotation() -- This is the main profile loop, any below this po
--- Interrupt ---
-----------------
if actionList.Interrupt() then return true end
- if not br._G.C_Spell.IsAutoRepeatSpell(br._G.GetSpellInfo(75)) and unit.exists(units.dyn40) and unit.distance(units.dyn40) < 40 then
+ if not br._G.C_Spell.IsAutoRepeatSpell(br.api.wow.GetSpellInfo(75)) and unit.exists(units.dyn40) and unit.distance(units.dyn40) < 40 then
br._G.StartAttack(units.dyn40)
end
@@ -298,9 +298,10 @@ local function runRotation() -- This is the main profile loop, any below this po
end -- Pause
end -- End runRotation
local id = 253 -- Change to the spec id profile is for. Spec ID can be found at: https://wowpedia.fandom.com/wiki/SpecializationID
+local expansion = br.isMOP
-- DO NOT EDIT ANYTHING BELOW THIS LINE, WILL BREAK PROFILE --
-if br.rotations[id] == nil then br.rotations[id] = {} end
-tinsert(br.rotations[id], {
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Hunter/Hunter/Hunter-Classic.lua b/Rotations/Hunter/Hunter/Hunter-Classic.lua
new file mode 100644
index 0000000000..e5c50f5b20
--- /dev/null
+++ b/Rotations/Hunter/Hunter/Hunter-Classic.lua
@@ -0,0 +1,246 @@
+local rotationName = "CuteOne" -- Change to name of profile listed in options drop down
+
+---------------
+--- Toggles ---
+---------------
+local function createToggles() -- Define custom toggles
+ -- Rotation Button
+ local RotationModes = {
+ [1] = { mode = "Auto", value = 1, overlay = "Automatic Rotation", tip = "Swaps between Single and Multiple based on number of #enemies.yards8 in range.", highlight = 1, icon = br.player.spells.swipe },
+ [2] = { mode = "Multi", value = 2, overlay = "Multiple Target Rotation", tip = "Forces Multiple Target Rotation.", highlight = 0, icon = br.player.spells.swipe },
+ [3] = { mode = "Single", value = 3, overlay = "Single Target Rotation", tip = "Forces Single Target Rotation.", highlight = 0, icon = br.player.spells.shred },
+ [4] = { mode = "Off", value = 4, overlay = "DPS Rotation Disabled", tip = "Disable DPS Rotation", highlight = 0, icon = br.player.spells.healingTouch }
+ };
+ br.ui:createToggle(RotationModes, "Rotation", 1, 0)
+ -- Defensive Button
+ local DefensiveModes = {
+ [1] = { mode = "On", value = 1, overlay = "Defensive Enabled", tip = "Includes Defensive Cooldowns.", highlight = 1, icon = br.player.spells.healingTouch },
+ [2] = { mode = "Off", value = 2, overlay = "Defensive Disabled", tip = "No Defensives will be used.", highlight = 0, icon = br.player.spells.healingTouch }
+ };
+ br.ui:createToggle(DefensiveModes, "Defensive", 2, 0)
+ -- Interrupt Button
+ local InterruptModes = {
+ [1] = { mode = "On", value = 1, overlay = "Interrupts Enabled", tip = "Includes Basic Interrupts.", highlight = 1, icon = br.player.spells.bash },
+ [2] = { mode = "Off", value = 2, overlay = "Interrupts Disabled", tip = "No Interrupts will be used.", highlight = 0, icon = br.player.spells.bash }
+ };
+ br.ui:createToggle(InterruptModes, "Interrupt", 3, 0)
+end
+
+---------------
+--- OPTIONS ---
+---------------
+local function createOptions()
+ local optionTable
+
+ local function rotationOptions()
+ local section
+ -----------------------
+ --- GENERAL OPTIONS ---
+ -----------------------
+ section = br.ui:createSection(br.ui.window.profile, "General")
+ br.ui:checkSectionState(section)
+ -------------------------
+ --- DEFENSIVE OPTIONS ---
+ -------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Defensive")
+ br.ui:checkSectionState(section)
+ -------------------------
+ --- INTERRUPT OPTIONS ---
+ -------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Interrupts")
+ -- Interrupt Percentage
+ br.ui:createSpinnerWithout(section, "Interrupt At", 0, 0, 95, 5,
+ "|cffFFFFFFCast Percent to Cast At (0 is random)")
+ br.ui:checkSectionState(section)
+ ----------------------
+ --- TOGGLE OPTIONS ---
+ ----------------------
+ section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
+ -- Single/Multi Toggle
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
+ -- Defensive Key Toggle
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
+ -- Interrupt Key Toggle
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
+ br.ui:checkSectionState(section)
+ end
+ optionTable = { {
+ [1] = "Rotation Options",
+ [2] = rotationOptions,
+ } }
+ return optionTable
+end
+
+--------------
+--- Locals ---
+--------------
+-- BR API Locals - Many of these are located from System/API, this is a sample of commonly used ones but no all inclusive
+local buff
+local cast
+local cd
+local debuff
+local enemies
+local module
+local ui
+local unit
+local units
+local spell
+local var = {}
+-- General Locals - Common Non-BR API Locals used in profiles
+local haltProfile
+local profileStop
+-- Profile Specific Locals - Any custom to profile locals
+local actionList = {}
+
+-----------------
+--- Functions --- -- List all profile specific custom functions here
+-----------------
+
+
+--------------------
+--- Action Lists ---
+--------------------
+-- Action List - Extra
+actionList.Extra = function()
+
+end -- End Action List - Extra
+
+-- Action List - Defensive
+actionList.Defensive = function()
+ if ui.useDefensive() then
+
+ end
+end -- End Action List - Defensive
+
+-- * Action List - Interrupts
+actionList.Interrupts = function()
+ if ui.useInterrupt() and ui.delay("Interrupts", unit.gcd(true)) then
+ local thisUnit
+ -- * Bash
+ if ui.checked("Bash - Int") then
+ for i = 1, #enemies.yards5f do
+ thisUnit = enemies.yards5f[i]
+ if unit.interruptable(thisUnit, ui.value("Interrupt At")) then
+
+ end
+ end
+ end
+ end -- End useInterrupts check
+end -- End Action List - Interrupts
+
+-- Action List - Cooldowns
+actionList.Cooldowns = function()
+ if ui.useCDs() then
+
+ end
+end -- End Action List - Cooldowns
+
+-- Action List - Pre-Combat
+actionList.PreCombat = function()
+ if not unit.inCombat() and not (unit.flying() or unit.mounted()) then
+ if unit.valid("target") then
+ local thisDistance = unit.distance("target") or 99
+
+ if thisDistance < 5 then
+ -- Auto Attack
+ if not cast.auto.autoAttack() and unit.valid("target") and not unit.deadOrGhost("target") and unit.distance("target") < 5 then
+ StartAttack()
+ ui.debug("Casting Auto Shot [Precombat]")
+ return true
+ end
+ end
+ end
+ end
+end -- End Action List - PreCombat
+
+-- Action List - Combat
+actionList.Combat = function()
+ if unit.inCombat() and unit.valid("target") then
+ local formValue = ui.mode.forms
+ ------------------------
+ --- In Combat - Main ---
+ ------------------------
+ -- Start Attack
+ if unit.exists(units.dyn5) and not cast.auto.autoAttack() and not unit.deadOrGhost("target") then
+ StartAttack()
+ ui.debug("Casting Auto Shot")
+ return true
+ end
+ end -- End In Combat Rotation
+end -- End Action list - Combat
+
+----------------
+--- ROTATION ---
+----------------
+local function runRotation()
+ ---------------------
+ --- Define Locals ---
+ ---------------------
+ -- BR API Locals
+ buff = br.player.buff
+ cast = br.player.cast
+ cd = br.player.cd
+ debuff = br.player.debuff
+ enemies = br.player.enemies
+ module = br.player.module
+ ui = br.player.ui
+ unit = br.player.unit
+ units = br.player.units
+ spell = br.player.spell
+ -- General Locals
+ profileStop = profileStop or false
+ haltProfile = (unit.inCombat() and profileStop) or ui.pause() or ui.mode.rotation == 4 or unit.id("target") == 156716
+ -- Units
+ units.get(5) -- Makes a variable called, units.dyn5
+ units.get(40) -- Makes a variable called, units.dyn40
+ units.get(40, true) -- Makes a variable called, units.dyn40AOE
+ -- Enemies
+ enemies.get(5) -- Makes a varaible called, enemies.yards5
+ enemies.get(20) -- Makes a varaible called, enemies.yards20
+ enemies.get(20, "player", true) -- makes enemies.yards20nc
+ enemies.get(40) -- Makes a varaible called, enemies.yards40
+
+ if not unit.inCombat() and not unit.exists("target") then
+ if profileStop then profileStop = false end
+ end
+
+ ---------------------
+ --- Begin Profile ---
+ ---------------------
+ -- Profile Stop | Pause
+ if not unit.inCombat() and not unit.exists("target") and profileStop then
+ profileStop = false
+ elseif haltProfile then
+ return true
+ else
+ ---------------------------------
+ --- Out Of Combat - Rotations ---
+ ---------------------------------
+ -----------------------
+ --- Extras Rotation ---
+ -----------------------
+ if actionList.Extra() then return true end
+ -----------------
+ --- Defensive ---
+ -----------------
+ if actionList.Defensive() then return true end
+ ------------------
+ --- Pre-Combat ---
+ ------------------
+ if actionList.PreCombat() then return true end
+ -----------------------------
+ --- In Combat - Rotations ---
+ -----------------------------
+ if actionList.Combat() then return true end
+ end -- Pause
+ return true
+end -- End runRotation
+local id = 361 -- Change to the spec id profile is for.
+local expansion = br.isClassic -- Change to the expansion the profile is for.
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
+ name = rotationName,
+ toggles = createToggles,
+ options = createOptions,
+ run = runRotation,
+})
diff --git a/Rotations/Hunter/Initial/InitialHunter.lua b/Rotations/Hunter/Initial/InitialHunter.lua
index 36cd0f2e23..2812395448 100644
--- a/Rotations/Hunter/Initial/InitialHunter.lua
+++ b/Rotations/Hunter/Initial/InitialHunter.lua
@@ -1,5 +1,5 @@
local rotationName = "Initial"
-br.loadSupport("PetCuteOne")
+br.loader.cBuilder:loadSupport("PetCuteOne")
---------------
--- Toggles ---
@@ -54,7 +54,7 @@ local function createOptions()
-------------------
--- PET OPTIONS ---
-------------------
- br.rotations.support["PetCuteOne"].options()
+ br.loader.rotations.support["PetCuteOne"].options()
-------------------------
--- DEFENSIVE OPTIONS ---
-------------------------
@@ -86,13 +86,13 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
--Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupts Key Toggle
- br.ui:createDropdownWithout(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
-- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -195,7 +195,7 @@ actionList.Interrupt = function()
if ui.useInterrupt() then
for i = 1, #enemies.yards5 do
local thisUnit = enemies.yards5[i]
- if br.canInterrupt(thisUnit, ui.value("Interrupt At")) then
+ if br.functions.spell:canInterrupt(thisUnit, ui.value("Interrupt At")) then
-- Freezing Trap
if ui.hecked("Freezing Trap") and cast.able.freezingTrap() then
for j = 1, #enemies.yards40 do
@@ -247,7 +247,7 @@ local function runRotation()
--- Load Additional Rotation Files ---
--------------------------------------
if actionList.PetManagement == nil then
- actionList.PetManagement = br.rotations.support["PetCuteOne"].run
+ actionList.PetManagement = br.loader.rotations.support["PetCuteOne"].run
end
---------------------
@@ -264,7 +264,7 @@ local function runRotation()
unit = br.player.unit
units = br.player.units
-- General Locals
- var.haltProfile = (unit.inCombat() and var.profileStop) or unit.mounted() or br.pause() or buff.feignDeath.exists() or
+ var.haltProfile = (unit.inCombat() and var.profileStop) or unit.mounted() or br.functions.misc:pause() or buff.feignDeath.exists() or
ui.mode.rotation == 2
-- Units
units.get(5)
@@ -343,8 +343,9 @@ local function runRotation()
end -- Pause
end -- End runRotation
local id = 1448
-if br.rotations[id] == nil then br.rotations[id] = {} end
-br._G.tinsert(br.rotations[id], {
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Hunter/Marksmanship/MarksmanshipCuteOne.lua b/Rotations/Hunter/Marksmanship/MarksmanshipCuteOne.lua
index b3decd09a7..d5c53dae27 100644
--- a/Rotations/Hunter/Marksmanship/MarksmanshipCuteOne.lua
+++ b/Rotations/Hunter/Marksmanship/MarksmanshipCuteOne.lua
@@ -13,7 +13,7 @@
-------------------------------------------------------
local rotationName = "CuteOne"
-br.loadSupport("PetCuteOne")
+br.loader.cBuilder:loadSupport("PetCuteOne")
---------------
--- Toggles ---
---------------
@@ -100,7 +100,7 @@ local function createOptions()
-------------------
--- Pet Options ---
-------------------
- br.rotations.support["PetCuteOne"].options()
+ br.loader.rotations.support["PetCuteOne"].options()
------------------------
--- Cooldown Options ---
------------------------
@@ -160,17 +160,17 @@ local function createOptions()
--------------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 6)
-- Cooldown Key Toggle
- br.ui:createDropdownWithout(section, "Cooldown Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 6)
-- Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupts Key Toggle
- br.ui:createDropdownWithout(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
-- Volley Key Toggle
- br.ui:createDropdownWithout(section, "Volley Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Volley Mode", br.ui.dropOptions.Toggle, 6)
-- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -872,7 +872,7 @@ local function runRotation()
--- Load Additional Rotation Files ---
--------------------------------------
if actionList.PetManagement == nil then
- actionList.PetManagement = br.rotations.support["PetCuteOne"].run
+ actionList.PetManagement = br.loader.rotations.support["PetCuteOne"].run
end
---------------
@@ -995,8 +995,9 @@ local function runRotation()
end --End Rotation Logic
end -- End runRotation
local id = 254
-if br.rotations[id] == nil then br.rotations[id] = {} end
-br._G.tinsert(br.rotations[id], {
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Hunter/Support/PetCuteOne.lua b/Rotations/Hunter/Support/PetCuteOne.lua
index 188729d852..c934247fd0 100644
--- a/Rotations/Hunter/Support/PetCuteOne.lua
+++ b/Rotations/Hunter/Support/PetCuteOne.lua
@@ -24,7 +24,7 @@ local petDead = false
local petRevived = false
local targetSwitchTimer = br._G.GetTime()
-br.rotations.support["PetCuteOne"] = {
+br.loader.rotations.support["PetCuteOne"] = {
options = function()
local alwaysCdAoENever = { "Always", "|cff008000AOE", "|cffffff00AOE/CD", "|cff0000ffCD", "|cffff0000Never" }
-- Pet Options
@@ -115,11 +115,11 @@ br.rotations.support["PetCuteOne"] = {
var.haltPetProfile = br._G.UnitCastingInfo("pet") or br._G.UnitHasVehicleUI("player") or
br._G.CanExitVehicle("player") or br._G.UnitOnTaxi("player") or unit.mounted() or unit.flying()
or paused or buff.feignDeath.exists() or buff.playDead.exists("pet") or
- (mode.rotation == 4 or (mode.rotation == 2 and br.selectedSpec == "Initial"))
+ (mode.rotation == 4 or (mode.rotation == 2 and br.loader.selectedSpec == "Initial"))
-- Pet Specific Locals
local callPet = spells["callPet" .. mode.petSummon]
local callPetName = mode.petSummon < 6 and select(2, br._G.GetCallPetSpellInfo(callPet)) or ""
- local friendUnit = br.friend[1].unit
+ local friendUnit = br.engines.healingEngine.friend[1].unit
local petActive = pet.active.exists()
local petDistance = unit.distance(br.petTarget, "pet") or 99
local petExists = unit.exists("pet")
@@ -149,7 +149,7 @@ br.rotations.support["PetCuteOne"] = {
enemies.yards40r = enemies.rect.get(10, 40, false)
-- Loot Count
- lootCount = br.lootManager:lootCount()
+ lootCount = br.engines.lootEngine:lootCount()
-- Pet Target Modes
if br.petTarget == nil then br.petTarget = "target" end
@@ -219,11 +219,11 @@ br.rotations.support["PetCuteOne"] = {
end
end
-- -- Call Pet
- -- if ((not br.deadPet and not petExists) or not petActive) and not buff.playDead.exists("pet") and not petCalled then
+ -- if ((not br.readers.combatLog.deadPet and not petExists) or not petActive) and not buff.playDead.exists("pet") and not petCalled then
-- if cast["callPet"..mode.petSummon]("player") then ui.debug("[Pet] Casting Call Pet") --[[ui.print("Hey "..callPetName.."...WAKE THE FUCK UP! It's already past noon!...GET YOUR LIFE TOGETHER!")]] petAppearTimer = br._G.GetTime(); petCalled = true; petRevived = false; return true end
-- end
-- -- Revive Pet
- -- if br.deadPet or (petExists and petHealth == 0) or petCalled == true then
+ -- if br.readers.combatLog.deadPet or (petExists and petHealth == 0) or petCalled == true then
-- if cast.able.revivePet("player") and cast.timeSinceLast.revivePet() > unit.gcd(true) then
-- if cast.revivePet("player") then ui.debug("[Pet] Casting Revive Pet") --[[ui.print("Hey "..callPetName.."...WAKE THE FUCK UP! It's already past noon!...GET YOUR LIFE TOGETHER!")]] petAppearTimer = br._G.GetTime(); petRevived = true; petCalled = false; return true end
-- end
@@ -303,7 +303,7 @@ br.rotations.support["PetCuteOne"] = {
end
-- Attack Abilities
if ui.checked("Use Attack Ability") and not var.haltPetProfile and unit.inCombat("pet") and validTarget and petDistance < 5
- and not br.isTotem(br.petTarget) and not buff.playDead.exists("pet")
+ and not br.engines.enemiesEngineFunctions:isTotem(br.petTarget) and not buff.playDead.exists("pet")
then
-- Bite
if cast.able.bite(br.petTarget, "pet") then
@@ -465,7 +465,7 @@ br.rotations.support["PetCuteOne"] = {
end
-- Debuff Abilities
if ui.checked("Use Debuff Ability") and not var.haltPetProfile and unit.inCombat("pet") and not buff.playDead.exists("pet")
- and validTarget and petDistance < 5 and not br.isTotem(br.petTarget) and debuff.mortalWounds.refresh(br.petTarget)
+ and validTarget and petDistance < 5 and not br.engines.enemiesEngineFunctions:isTotem(br.petTarget) and debuff.mortalWounds.refresh(br.petTarget)
then
-- Acid Bite
if cast.able.acidBite(br.petTarget) then
@@ -555,7 +555,7 @@ br.rotations.support["PetCuteOne"] = {
-- Heal Abilities
if ui.checked("Use Heal Ability") and not buff.playDead.exists("pet") then
-- Eternal Guardian
- if cast.able.eternalGuardian() and (br.deadPet or (petExists and petHealth == 0)) then
+ if cast.able.eternalGuardian() and (br.readers.combatLog.deadPet or (petExists and petHealth == 0)) then
if cast.eternalGuardian() then
ui.debug("[Pet] Cast Eternal Guardian")
return true
@@ -591,7 +591,7 @@ br.rotations.support["PetCuteOne"] = {
for i = 1, #enemies.yards5p do
local thisUnit = enemies.yards5p[i]
if ui.value("Purge") == 1 or (ui.value("Purge") == 2 and unit.isUnit(thisUnit, "target")) then
- if unit.valid(thisUnit) and cast.dispel.spiritPulse(thisUnit) then --br.canDispel(thisUnit,spells.spiritPulse) then
+ if unit.valid(thisUnit) and cast.dispel.spiritPulse(thisUnit) then --br.functions.aura:canDispel(thisUnit,spells.spiritPulse) then
if cast.able.spiritPulse(thisUnit, "pet") then
if cast.spiritPulse(thisUnit, "pet") then
ui.debug("[Pet] Cast Spirit Pulse")
@@ -684,11 +684,11 @@ br.rotations.support["PetCuteOne"] = {
end
-- Fetch
if ui.checked("Fetch") and not unit.inCombat() and not unit.inCombat("pet") and cast.able.fetch("pet") and not cast.current.fetch("pet")
- and petExists and not br.deadPet and cast.timeSinceLast.fetch() > unit.gcd(true) * 2 and not buff.playDead.exists("pet")
+ and petExists and not br.readers.combatLog.deadPet and cast.timeSinceLast.fetch() > unit.gcd(true) * 2 and not buff.playDead.exists("pet")
then
if fetching and (lootCount == 0) then fetching = false end
if not fetching then
- for _, v in pairs(br.lootable) do
+ for _, v in pairs(br.engines.enemiesEngine.lootable) do
local thisDistance = unit.distance(v.unit)
if thisDistance > 8 and thisDistance < 40 then
if cast.fetch("pet") then
@@ -701,7 +701,7 @@ br.rotations.support["PetCuteOne"] = {
end
end
-- Mend Pet
- if ui.checked("Mend Pet") and cast.able.mendPet("pet") and petExists and not br.deadPet
+ if ui.checked("Mend Pet") and cast.able.mendPet("pet") and petExists and not br.readers.combatLog.deadPet
and not buff.mendPet.exists("pet") and petHealth < ui.value("Mend Pet")
then
if cast.mendPet("pet") then
@@ -710,7 +710,7 @@ br.rotations.support["PetCuteOne"] = {
end
end
-- Play Dead / Wake Up
- if ui.checked("Play Dead / Wake Up") and not br.deadPet then
+ if ui.checked("Play Dead / Wake Up") and not br.readers.combatLog.deadPet then
if cast.able.playDead("player") and unit.inCombat("pet") and not buff.playDead.exists("pet")
and petHealth < ui.value("Play Dead / Wake Up")
then
@@ -719,7 +719,7 @@ br.rotations.support["PetCuteOne"] = {
return true
end
end
- if ui.checked("Play Dead / Wake Up") and not br.deadPet then
+ if ui.checked("Play Dead / Wake Up") and not br.readers.combatLog.deadPet then
var.woke = ui.value("Play Dead / Wake Up") > 50 and 100 or 50
if cast.able.wakeUp("player") and buff.playDead.exists("pet") and not buff.feignDeath.exists()
and petHealth >= var.woke
diff --git a/Rotations/Mage/Initial/CronoMageInitial.lua b/Rotations/Mage/Initial/CronoMageInitial.lua
index 57ef038b8d..dec67b3e8e 100644
--- a/Rotations/Mage/Initial/CronoMageInitial.lua
+++ b/Rotations/Mage/Initial/CronoMageInitial.lua
@@ -58,13 +58,13 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdown(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdown(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
--Defensive Key Toggle
- br.ui:createDropdown(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupts Key Toggle
- br.ui:createDropdown(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
-- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -173,7 +173,7 @@ local function runRotation()
units = br.player.units
-- General Locals
profileStop = profileStop or false
- haltProfile = (unit.inCombat() and profileStop) or br._G.IsMounted() or br.pause() or mode.rotation == 4
+ haltProfile = (unit.inCombat() and profileStop) or br._G.IsMounted() or br.functions.misc:pause() or mode.rotation == 4
-- Units
units.get(5) -- Makes a variable called, units.dyn5
units.get(40) -- Makes a variable called, units.dyn40
@@ -222,7 +222,7 @@ local function runRotation()
if actionList.Interrupt() then return true end
-- Start Attack
-- actions=auto_attack
- if not br._G.C_Spell.IsAutoRepeatSpell(br._G.GetSpellInfo(6603)) and unit.exists(units.dyn5) and unit.distance(units.dyn5) < 5 then
+ if not br._G.C_Spell.IsAutoRepeatSpell(br.api.wow.GetSpellInfo(6603)) and unit.exists(units.dyn5) and unit.distance(units.dyn5) < 5 then
br._G.StartAttack(units.dyn5)
end
--Arcane Explosion
@@ -250,10 +250,11 @@ local function runRotation()
end
end -- Pause
end -- End runRotation
-local id = 1449 -- Change to the spec id profile is for. Spec ID can be found at: https://wowpedia.fandom.com/wiki/SpecializationID
+local id = 0 --1449 -- Change to the spec id profile is for. Spec ID can be found at: https://wowpedia.fandom.com/wiki/SpecializationID
+local expansion = br.isMOP
-- DO NOT EDIT ANYTHING BELOW THIS LINE, WILL BREAK PROFILE --
-if br.rotations[id] == nil then br.rotations[id] = {} end
-br._G.tinsert(br.rotations[id], {
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Mage/Initial/MageInitial.lua b/Rotations/Mage/Initial/MageInitial-MOP.lua
similarity index 80%
rename from Rotations/Mage/Initial/MageInitial.lua
rename to Rotations/Mage/Initial/MageInitial-MOP.lua
index 899f18682a..826c3e9022 100644
--- a/Rotations/Mage/Initial/MageInitial.lua
+++ b/Rotations/Mage/Initial/MageInitial-MOP.lua
@@ -6,8 +6,8 @@ local rotationName = "Initial"
local function createToggles()
-- Rotation Button
local RotationModes = {
- [1] = { mode = "On", value = 1, overlay = "Rotation Enabled", tip = "Enable Rotation", highlight = 1, icon = br.player.spells.frostBolt },
- [2] = { mode = "Off", value = 4, overlay = "Rotation Disabled", tip = "Disable Rotation", highlight = 0, icon = br.player.spells.frostBolt }
+ [1] = { mode = "On", value = 1, overlay = "Rotation Enabled", tip = "Enable Rotation", highlight = 1, icon = br.player.spells.frostfireBolt },
+ [2] = { mode = "Off", value = 4, overlay = "Rotation Disabled", tip = "Disable Rotation", highlight = 0, icon = br.player.spells.frostfireBolt }
};
br.ui:createToggle(RotationModes, "Rotation", 1, 0)
-- Defensive Button
@@ -36,8 +36,7 @@ local function createOptions()
--- GENERAL OPTIONS ---
-----------------------
section = br.ui:createSection(br.ui.window.profile, "General")
- -- Arcane Intellect
- br.ui:createCheckbox(section, "Arcane Intellect")
+
br.ui:checkSectionState(section)
-------------------------
--- DEFENSIVE OPTIONS ---
@@ -45,8 +44,6 @@ local function createOptions()
section = br.ui:createSection(br.ui.window.profile, "Defensive")
-- Forst Nova
br.ui:createSpinner(section, "Frost Nova", 30, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
- --Invisibility
- br.ui:createSpinner(section, "Invisibility", 25, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
br.ui:checkSectionState(section)
-------------------------
--- INTERRUPT OPTIONS ---
@@ -62,13 +59,11 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
--Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupts Key Toggle
- br.ui:createDropdownWithout(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
- -- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -99,13 +94,7 @@ local actionList = {}
--------------------
-- Action List - Extras
actionList.Extras = function()
- -- Arcane Intellect
- if ui.checked("Arcane Intellect") and cast.able.arcaneIntellect("player") and not buff.arcaneIntellect.exists() then
- if cast.arcaneIntellect("player") then
- ui.debug("Casting Arcane Intellect")
- return true
- end
- end
+
end
-- Action List - Defensive
actionList.Defensive = function()
@@ -116,13 +105,6 @@ actionList.Defensive = function()
return true
end
end
- -- Invisibility
- if ui.checked("Invisibility") and cast.able.invisibility() and unit.hp() < ui.value("Invisibility") then
- if cast.invisibility() then
- ui.debug("Casting Invisibility")
- return true
- end
- end
end -- End Action List - Defensive
-- Action List - Interrupts
@@ -130,9 +112,9 @@ actionList.Interrupts = function()
if ui.useInterrupt() then
for i = 1, #enemies.yards40 do
local thisUnit = enemies.yards40[i]
- if br.canInterrupt(thisUnit, ui.value("Interrupt At")) then
+ if br.functions.spell:canInterrupt(thisUnit, ui.value("Interrupt At")) then
-- Counterspell
- if br.canInterrupt() then
+ if br.functions.spell:canInterrupt() then
if cast.able.counterspell(thisUnit) and unit.distance(thisUnit) then
if cast.counterspell(thisUnit) then
ui.debug("Casting Counterspell")
@@ -151,9 +133,9 @@ actionList.PreCombat = function()
if unit.valid("target") then
local thisDistance = unit.distance("target") or 99
if not unit.moving() and thisDistance < 40 then
- if cast.able.frostbolt("target") and (unit.level() < 2 or not cast.last.frostbolt() or cast.timeSinceLast.frostbolt() > unit.gcd(true) + 0.5) then
- if cast.frostbolt("target") then
- ui.debug("Casting Frostbolt [Pre-Pull]")
+ if cast.able.frostfireBolt("target") and (unit.level() < 2 or not cast.last.frostfireBolt() or cast.timeSinceLast.frostfireBolt() > unit.gcd(true) + 0.5) then
+ if cast.frostfireBolt("target") then
+ ui.debug("Casting Frostfire Bolt [Pre-Pull]")
return true
end
end
@@ -182,13 +164,6 @@ actionList.Combat = function()
return true
end
end
- -- Arcane Explosion
- if cast.able.arcaneExplosion("player", "aoe", 3, 10) then
- if cast.arcaneExplosion("player", "aoe", 3, 10) then
- ui.debug("Casting Arcane Explosion")
- return true
- end
- end
-- Fire Blast
if cast.able.fireBlast() then
if cast.fireBlast() then
@@ -196,10 +171,10 @@ actionList.Combat = function()
return true
end
end
- -- Frost Bolt
- if cast.able.frostBolt() and not unit.moving() then
- if cast.frostBolt() then
- ui.debug("Casting Frost Bolt")
+ -- Frostfire Bolt
+ if cast.able.frostfireBolt() and not unit.moving() then
+ if cast.frostfireBolt() then
+ ui.debug("Casting Frostfire Bolt")
return true
end
end
@@ -224,7 +199,7 @@ local function runRotation()
units = br.player.units
-- General Locals
profileStop = profileStop or false
- haltProfile = (unit.inCombat() and profileStop) or unit.mounted() or br.pause() or ui.mode.rotation == 2
+ haltProfile = (unit.inCombat() and profileStop) or unit.mounted() or br.functions.misc:pause() or ui.mode.rotation == 2
-- Units
units.get(40) -- Makes a variable called, units.dyn40
units.get(40, true)
@@ -270,8 +245,9 @@ local function runRotation()
end -- Pause
end -- End runRotation
local id = 1449 -- Change to the spec id profile is for.
-if br.rotations[id] == nil then br.rotations[id] = {} end
-br._G.tinsert(br.rotations[id], {
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Monk/BrewMaster/BrewBrewMaster.lua b/Rotations/Monk/BrewMaster/BrewBrewMaster.lua
index 8dda9b2778..0c9bcc654b 100644
--- a/Rotations/Monk/BrewMaster/BrewBrewMaster.lua
+++ b/Rotations/Monk/BrewMaster/BrewBrewMaster.lua
@@ -147,15 +147,15 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdown(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdown(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
--Cooldown Key Toggle
- br.ui:createDropdown(section, "Cooldown Mode", br.dropOptions.Toggle, 3)
+ br.ui:createDropdown(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 3)
--Defensive Key Toggle
- br.ui:createDropdown(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupts Key Toggle
- br.ui:createDropdown(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
-- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = {{
@@ -464,7 +464,7 @@ local function runRotation() -- This is the main profile loop, any below this po
var = br.player.variables
debuff = br.player.debuff
- ui.mode.debug = br.data.settings[br.selectedSpec].toggles["DebugMode"]
+ ui.mode.debug = br.data.settings[br.loader.selectedSpec].toggles["DebugMode"]
--TODO this needs to be replaced with some sort of real calculation, in the mean time we're just looking for having cast Purifying Brew in the last 10 seconds
var.recent_purifies = cast.last.purifyingBrew(10)
@@ -503,10 +503,10 @@ local function runRotation() -- This is the main profile loop, any below this po
end
if ui.mode.debug==1 then
- local n,r,i,ct,mr,mxr = GetSpellInfo("Recent Purifies")
+ local n,r,i,ct,mr,mxr = br._G.GetSpellInfo("Recent Purifies")
for i=1,40 do
- local name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellIdbuff = UnitBuff("player", i);
+ local name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellIdbuff = br._G.UnitBuff("player", i);
if name then
if name=="Recent Purifies" then
print(name,spellIdbuff)
@@ -643,9 +643,10 @@ local function runRotation() -- This is the main profile loop, any below this po
end -- Pause
end -- End runRotation
local id = 268 -- Change to the spec id profile is for. Spec ID can be found at: https://wowpedia.fandom.com/wiki/SpecializationID
+local expansion = br.isMOP
-- DO NOT EDIT ANYTHING BELOW THIS LINE, WILL BREAK PROFILE --
-if br.rotations[id] == nil then br.rotations[id] = {} end
-tinsert(br.rotations[id],{
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+tinsert(br.loader.rotations[id],{
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Monk/Initial/InitialMonk.lua b/Rotations/Monk/Initial/InitialMonk.lua
index db1da97db2..69edefeab1 100644
--- a/Rotations/Monk/Initial/InitialMonk.lua
+++ b/Rotations/Monk/Initial/InitialMonk.lua
@@ -75,12 +75,16 @@ local function createOptions()
-- Vivify
br.ui:createSpinner(section, "Vivify", 60, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
-- Target Heal Key
- br.ui:createDropdownWithout(section, "Target Heal Key", br.dropOptions.Toggle, 2)
+ br.ui:createDropdownWithout(section, "Target Heal Key", br.ui.dropOptions.Toggle, 2)
br.ui:checkSectionState(section)
-------------------------
--- INTERRUPT OPTIONS ---
-------------------------
section = br.ui:createSection(br.ui.window.profile, "Interrupts")
+ -- Pandaren Racial
+ if select(2, UnitRace("player")) == "Pandaren" then
+ br.ui:createCheckbox(section, "Quaking Palm", "|cffFFBB00Pandaren Racial")
+ end
-- Leg Sweep
br.ui:createCheckbox(section, "Leg Sweep")
-- Interrupt Percentage
@@ -91,15 +95,15 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
-- Cooldown Key Toggle
- br.ui:createDropdownWithout(section, "Cooldown Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 6)
-- Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupts Key Toggle
- br.ui:createDropdownWithout(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
-- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -113,17 +117,21 @@ end
--- Locals ---
--------------
-- BR API Locals
+local buff
local cast
local cd
+local chi
local enemies
+local energy
local module
local ui
local unit
local units
+local spell
-- Profile Specific Locals
local actionList = {}
local var = {}
-var.getFacingDistance = br["getFacingDistance"]
+var.getFacingDistance = br.functions.range.getFacingDistance
var.haltProfile = false
var.profileStop = false
@@ -133,15 +141,15 @@ var.profileStop = false
-- Action List - Extra
actionList.Extra = function()
-- Crackling Jade Lightning
- if ui.checked("Crackling Jade Lightning") and not unit.mounted() and not cast.current.cracklingJadeLightning()
- and not unit.moving() and cast.able.cracklingJadeLightning("target") and unit.valid("target")
- and unit.distance("target") > ui.value("Cancel CJL Range")
- then
- if cast.cracklingJadeLightning("target") then
- ui.debug("Casting Crackling Jade Lightning")
- return true
- end
- end
+ -- if ui.checked("Crackling Jade Lightning") and not unit.mounted() and not cast.current.cracklingJadeLightning()
+ -- and not unit.moving() and cast.able.cracklingJadeLightning("target") and unit.valid("target")
+ -- and unit.distance("target") > ui.value("Cancel CJL Range")
+ -- then
+ -- if cast.cracklingJadeLightning("target") then
+ -- ui.debug("Casting Crackling Jade Lightning")
+ -- return true
+ -- end
+ -- end
-- Roll
if ui.checked("Roll") and cast.able.roll() and unit.moving()
and unit.distance("target") > 10 and unit.valid("target")
@@ -159,52 +167,52 @@ actionList.Defensive = function()
if ui.useDefensive() then
-- Basic Healing Module
module.BasicHealing()
- -- Expel Harm
- if ui.checked("Expel Harm") and cast.able.expelHarm() and unit.hp() <= ui.value("Expel Harm") then
- if cast.expelHarm() then
- ui.debug("Casting Expel Harm")
- return true
- end
- end
- -- Leg Sweep
- if ui.checked("Leg Sweep - HP") and cast.able.legSweep() and unit.hp() <= ui.value("Leg Sweep - HP")
- and unit.inCombat() and #enemies.yards5 > 0
- then
- if cast.legSweep() then
- ui.debug("Casting Leg Sweep [HP]")
- return true
- end
- end
- if ui.checked("Leg Sweep - AoE") and cast.able.legSweep()
- and #enemies.yards5 >= ui.value("Leg Sweep - AoE")
- then
- if cast.legSweep() then
- ui.debug("Casting Leg Sweep [AOE]")
- return true
- end
- end
- -- Vivify
- local vivifyUnit = unit.friend("target") and "target" or "player"
- if ui.checked("Vivify") and cast.able.vivify(vivifyUnit) then
- if unit.hp(vivifyUnit) <= ui.value("Vivify") then
- if cast.vivify(vivifyUnit) then
- ui.debug("Casting Vivify on " .. unit.name(vivifyUnit))
- return true
- end
- end
- end
+ -- -- Expel Harm
+ -- if ui.checked("Expel Harm") and cast.able.expelHarm() and unit.hp() <= ui.value("Expel Harm") then
+ -- if cast.expelHarm() then
+ -- ui.debug("Casting Expel Harm")
+ -- return true
+ -- end
+ -- end
+ -- -- Leg Sweep
+ -- if ui.checked("Leg Sweep - HP") and cast.able.legSweep() and unit.hp() <= ui.value("Leg Sweep - HP")
+ -- and unit.inCombat() and #enemies.yards5 > 0
+ -- then
+ -- if cast.legSweep() then
+ -- ui.debug("Casting Leg Sweep [HP]")
+ -- return true
+ -- end
+ -- end
+ -- if ui.checked("Leg Sweep - AoE") and cast.able.legSweep()
+ -- and #enemies.yards5 >= ui.value("Leg Sweep - AoE")
+ -- then
+ -- if cast.legSweep() then
+ -- ui.debug("Casting Leg Sweep [AOE]")
+ -- return true
+ -- end
+ -- end
+ -- -- Vivify
+ -- local vivifyUnit = unit.friend("target") and "target" or "player"
+ -- if ui.checked("Vivify") and cast.able.vivify(vivifyUnit) then
+ -- if unit.hp(vivifyUnit) <= ui.value("Vivify") then
+ -- if cast.vivify(vivifyUnit) then
+ -- ui.debug("Casting Vivify on " .. unit.name(vivifyUnit))
+ -- return true
+ -- end
+ -- end
+ -- end
end
end -- End Action List - Defensive
-- Action List - Cooldowns
actionList.Cooldowns = function()
-- Touch of Death
- if ui.alwaysCdNever("Touch of Death") and cast.able.touchOfDeath(units.dyn5) and unit.health(units.dyn5) < unit.healthMax("player") then
- if cast.touchOfDeath(units.dyn5) then
- ui.debug("Casting Touch of Death - Omae wa mou shindeiru")
- return true
- end
- end
+ -- if ui.alwaysCdNever("Touch of Death") and cast.able.touchOfDeath(units.dyn5) and unit.health(units.dyn5) < unit.healthMax("player") then
+ -- if cast.touchOfDeath(units.dyn5) then
+ -- ui.debug("Casting Touch of Death - Omae wa mou shindeiru")
+ -- return true
+ -- end
+ -- end
-- Trinket - Non-Specific
if unit.exists(units.dyn5) and unit.distance(units.dyn5) < 5 then
module.BasicTrinkets()
@@ -216,14 +224,21 @@ actionList.Interrupt = function()
if ui.useInterrupt() then
for i = 1, #enemies.yards5 do
local thisUnit = enemies.yards5[i]
- if br.canInterrupt(thisUnit, ui.value("Interrupt At")) then
- -- Leg Sweep
- if ui.checked("Leg Sweep") and cast.able.legSweep(thisUnit) and unit.distance(thisUnit) < 5 then
- if cast.legSweep(thisUnit) then
- ui.debug("Casting Leg Sweep [Interrupt]")
+ if br.functions.spell:canInterrupt(thisUnit, ui.value("Interrupt At")) then
+ -- Quaking Palm (Pandaren Racial)
+ if ui.checked("Quaking Palm") and unit.race("Pandaren") and cast.able.racial(thisUnit) and unit.distance(thisUnit) < 5 then
+ if cast.racial(thisUnit) then
+ ui.debug("Casting Quaking Palm [Interrupt]")
return true
end
end
+ -- Leg Sweep
+ -- if ui.checked("Leg Sweep") and cast.able.legSweep(thisUnit) and unit.distance(thisUnit) < 5 then
+ -- if cast.legSweep(thisUnit) then
+ -- ui.debug("Casting Leg Sweep [Interrupt]")
+ -- return true
+ -- end
+ -- end
end
end
end -- End Interrupt Check
@@ -234,8 +249,8 @@ actionList.PreCombat = function()
if not unit.inCombat() and not unit.mounted() then
if unit.valid("target") then
-- Auto Attack
- if cast.able.autoAttack() and unit.exists(units.dyn5) and unit.distance(units.dyn5) < 5 then
- if cast.autoAttack() then
+ if cast.able.autoAttack("target") and unit.exists("target") and unit.distance("target") < 5 then
+ if cast.autoAttack("target") then
ui.debug("Casting Auto Attack [Precombat]")
return true
end
@@ -258,27 +273,34 @@ actionList.Combat = function()
end
-- Call Action List - Cooldowns
if actionList.Cooldowns() then return true end
- -- Spinning Crane Kick
- if cast.able.spinningCraneKick() and #enemies.yards8 > 3 then
- if cast.spinningCraneKick() then
- ui.debug("Casting Spinning Crane Kick")
- return true
- end
- end
+ -- -- Spinning Crane Kick
+ -- if cast.able.spinningCraneKick() and #enemies.yards8 > 3 then
+ -- if cast.spinningCraneKick() then
+ -- ui.debug("Casting Spinning Crane Kick")
+ -- return true
+ -- end
+ -- end
-- Blackout Kick
- if cast.able.blackoutKick() then
+ if cast.able.blackoutKick() and buff.tigerPower.exists() then
if cast.blackoutKick() then
ui.debug("Casting Blackout Kick")
return true
end
end
-- Tiger Palm
- if cast.able.tigerPalm() and #enemies.yards8 < 4 then
+ if cast.able.tigerPalm() and (buff.tigerPower.refresh() or (chi() >= chi.max() - 1) or energy() < 40) then
if cast.tigerPalm() then
ui.debug("Casting Tiger Palm")
return true
end
end
+ -- Jab
+ if cast.able.jab() then
+ if cast.jab() then
+ ui.debug("Casting Jab")
+ return true
+ end
+ end
end -- End In Combat Rotation
end
end -- End Action List - Combat
@@ -291,15 +313,19 @@ local function runRotation()
--- Define Locals ---
---------------------
-- BR API Locals
+ buff = br.player.buff
cast = br.player.cast
cd = br.player.cd
+ chi = br.player.power.chi
enemies = br.player.enemies
+ energy = br.player.power.energy
module = br.player.module
ui = br.player.ui
unit = br.player.unit
units = br.player.units
+ spell = br.player.spell
-- General Locals
- var.haltProfile = (unit.inCombat() and var.profileStop) or unit.mounted() or br.pause() or ui.mode.rotation == 2
+ var.haltProfile = (unit.inCombat() and var.profileStop) or unit.mounted() or br.functions.misc:pause() or ui.mode.rotation == 2
-- Units
units.get(5)
units.get(40)
@@ -308,12 +334,12 @@ local function runRotation()
enemies.get(8)
-- Cancel Crackling Jade Lightning
- if cast.current.cracklingJadeLightning() and unit.distance("target") < ui.value("Cancel CJL Range") then
- if cast.cancel.cracklingJadeLightning() then
- ui.debug("Canceling Crackling Jade Lightning [Within " .. ui.value("Cancel CJL Range") .. "yrds]")
- return true
- end
- end
+ -- if cast.current.cracklingJadeLightning() and unit.distance("target") < ui.value("Cancel CJL Range") then
+ -- if cast.cancel.cracklingJadeLightning() then
+ -- ui.debug("Canceling Crackling Jade Lightning [Within " .. ui.value("Cancel CJL Range") .. "yrds]")
+ -- return true
+ -- end
+ -- end
---------------------
--- Begin Profile ---
@@ -352,8 +378,9 @@ local function runRotation()
end -- Pause
end -- End runRotation
local id = 1450
-if br.rotations[id] == nil then br.rotations[id] = {} end
-br._G.tinsert(br.rotations[id], {
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Monk/MistWeaver/BrewMistweaver.lua b/Rotations/Monk/MistWeaver/BrewMistweaver.lua
index b01bf0df29..814bce402e 100644
--- a/Rotations/Monk/MistWeaver/BrewMistweaver.lua
+++ b/Rotations/Monk/MistWeaver/BrewMistweaver.lua
@@ -527,7 +527,7 @@ end
local getRealHP = function(Unit)
if Unit ~= nil then
- if br.GetObjectExists(Unit) and br.GetUnitIsVisible(Unit) and not br.GetUnitIsDeadOrGhost(Unit) then
+ if br.functions.unit:GetObjectExists(Unit) and br.functions.unit:GetUnitIsVisible(Unit) and not br.functions.unit:GetUnitIsDeadOrGhost(Unit) then
return 100 * (br._G.UnitHealth(Unit) + br._G.UnitGetIncomingHeals(Unit, "player")) /
br._G.UnitHealthMax(Unit)
end
@@ -558,7 +558,7 @@ end
local getHPDefecit = function(Unit)
if Unit == nil then Unit = "player" end
- if br.GetObjectExists(Unit) and br.GetUnitIsVisible(Unit) and not br.GetUnitIsDeadOrGhost(Unit) then
+ if br.functions.unit:GetObjectExists(Unit) and br.functions.unit:GetUnitIsVisible(Unit) and not br.functions.unit:GetUnitIsDeadOrGhost(Unit) then
return ((br._G.UnitHealthMax(Unit) - br._G.UnitHealth(Unit)) + br._G.UnitGetIncomingHeals(Unit, "player"))
else
return 0
@@ -983,7 +983,7 @@ local actionList = {
if br.player.ui.mode.content == 1 then
-- Chi Burst
if ui.checked(text.heal.chiBurst) and cd.chiBurst.ready() and talent.chiBurst and not player.isMoving then
- local lowAlliesTargetsChiBurst = br.getUnitsInRect(7, 40, false, ui.value(text.heal.chiBurst .. "2"))
+ local lowAlliesTargetsChiBurst = br.engines.healingEngineFunctions:getUnitsInRect(7, 40, false, ui.value(text.heal.chiBurst .. "2"))
if lowAlliesTargetsChiBurst >= ui.value(text.heal.chiBurst .. "1") and cast.able.chiBurst(player.unit) then
if cast.chiBurst() then
ui.debug("[SUCCESS]: " .. text.heal.chiBurst)
@@ -1003,7 +1003,7 @@ local actionList = {
-- Enveloping Mist
if ui.checked(text.heal.envelopingBreath) and cd.envelopingMist.ready() and not player.isMoving then
if totemInfo.yulonDuration > cast.time.envelopingMist() + br.getLatency() or totemInfo.chiJiDuration > cast.time.envelopingMist() + br.getLatency() then
- local lowHealthAroundUnit = br.getUnitsToHealAround(friends.lowest.unit, 7.5,
+ local lowHealthAroundUnit = br.engines.healingEngineFunctions:getUnitsToHealAround(friends.lowest.unit, 7.5,
ui.value(text.heal.envelopingBreath .. "2"), 6)
if #lowHealthAroundUnit >= ui.value(text.heal.envelopingBreath .. "1") and cast.able.envelopingMist(friends.lowest.unit) then
if cast.envelopingMist(friends.lowest.unit) then
@@ -1025,7 +1025,7 @@ local actionList = {
elseif br.player.ui.mode.content == 2 then
-- Chi Burst
if ui.checked(text2.heal2.chiBurst2) and cd.chiBurst.ready() and talent.chiBurst and not player.isMoving then
- local lowAlliesTargetsChiBurst = br.getUnitsInRect(7, 40, false, ui.value(text2.heal2.chiBurst2 ..
+ local lowAlliesTargetsChiBurst = br.engines.healingEngineFunctions:getUnitsInRect(7, 40, false, ui.value(text2.heal2.chiBurst2 ..
"2"))
if lowAlliesTargetsChiBurst >= ui.value(text2.heal2.chiBurst2 .. "1") and cast.able.chiBurst() then
if cast.chiBurst() then
@@ -1046,7 +1046,7 @@ local actionList = {
-- Enveloping Breath
if ui.checked(text2.heal2.envelopingBreath2) and cd.envelopingMist.ready() and not player.isMoving then
if totemInfo.yulonDuration > cast.time.envelopingMist() + br.getLatency() or totemInfo.chiJiDuration > cast.time.envelopingMist() + br.getLatency() then
- local lowHealthAroundUnit = br.getUnitsToHealAround(friends.lowest.unit, 7.5,
+ local lowHealthAroundUnit = br.engines.healingEngineFunctions:getUnitsToHealAround(friends.lowest.unit, 7.5,
ui.value(text2.heal2.envelopingBreath2 .. "2"), 6)
if #lowHealthAroundUnit >= ui.value(text2.heal2.envelopingBreath2 .. "1") and cast.able.envelopingMist(friends.lowest.unit) and
cast.able.envelopingMist(friends.lowest.unit) then
@@ -1072,7 +1072,7 @@ local actionList = {
faelineStompRotation = function()
if br.player.ui.mode.content == 1 then
if ui.checked(text.heal.faelineStomp) and cd.faelineStomp.ready() and talent.faelineStomp then
- local lowAlliesTargetsfaelineStomp = br.getUnitsInRect(7, 40, false,
+ local lowAlliesTargetsfaelineStomp = br.engines.healingEngineFunctions:getUnitsInRect(7, 40, false,
ui.value(text.heal.faelineStomp .. "2"))
if lowAlliesTargetsfaelineStomp >= ui.value(text.heal.faelineStomp .. "1") and cast.able.faelineStomp() then
if cast.faelineStomp() then
@@ -1083,7 +1083,7 @@ local actionList = {
end
elseif br.player.ui.mode.content == 2 then
if ui.checked(text2.heal2.faelineStomp2) and cd.faelineStomp.ready() and talent.faelineStomp then
- local lowAlliesTargetsfaelineStomp2 = br.getUnitsInRect(7, 40, false,
+ local lowAlliesTargetsfaelineStomp2 = br.engines.healingEngineFunctions:getUnitsInRect(7, 40, false,
ui.value(text2.heal2.faelineStomp2 .. "2"))
if lowAlliesTargetsfaelineStomp2 >= ui.value(text2.heal2.faelineStomp2 .. "1") and cast.able.faelineStomp() then
if cast.faelineStomp(player.unit) then
@@ -1098,7 +1098,7 @@ local actionList = {
zenPulseRotation = function()
if br.player.ui.mode.content == 1 then
if ui.checked(text.heal.zenPulse) and cd.zenPulse.ready() and talent.zenPulse then
- local lowAlliesTargetszenPulse = br.getUnitsInRect(7, 40, false, ui.value(text.heal.zenPulse .. "2"))
+ local lowAlliesTargetszenPulse = br.engines.healingEngineFunctions:getUnitsInRect(7, 40, false, ui.value(text.heal.zenPulse .. "2"))
if lowAlliesTargetszenPulse >= ui.value(text.heal.zenPulse .. "1") and cast.able.zenPulse(friends.lowest.unit) then
if cast.zenPulse(friends.lowest.unit) then
ui.debug(buildSingleActionMessage(friends.lowest, text.heal.zenPulse))
@@ -1108,7 +1108,7 @@ local actionList = {
end
elseif br.player.ui.mode.content == 2 then
if ui.checked(text.heal.zenPulse2) and cd.zenPulse.ready() and talent.zenPulse then
- local lowAlliesTargetszenPulse2 = br.getUnitsInRect(7, 40, false,
+ local lowAlliesTargetszenPulse2 = br.engines.healingEngineFunctions:getUnitsInRect(7, 40, false,
ui.value(text2.heal2.zenPulse2 .. "2"))
if lowAlliesTargetszenPulse2 >= ui.value(text2.heal2.zenPulse2 .. "1") and cast.able.zenPulse(friends.lowest.unit) then
if cast.zenPulse(friends.lowest.unit) then
@@ -1352,12 +1352,12 @@ local actionList = {
cd.summonJadeSerpentStatue.ready() and unit.inCombat("player") and not player.isMoving then
local aroundUnit
if tanks[1] ~= nil then
- if ui.value(text.utility.summonJadeSerpentStatue) == 1 and #br.friend > 1 then
+ if ui.value(text.utility.summonJadeSerpentStatue) == 1 and #br.engines.healingEngine.friend > 1 then
aroundUnit = tanks[1].unit
else
aroundUnit = player.unit
end
- local tx, ty, tz = br.GetObjectPosition(aroundUnit)
+ local tx, ty, tz = br.functions.unit:GetObjectPosition(aroundUnit)
local results = sqrt(
((tx - summonJadeSerpentStatuePosition.x) ^ 2) +
((ty - summonJadeSerpentStatuePosition.y) ^ 2) +
@@ -1365,12 +1365,12 @@ local actionList = {
)
local distanceToStatue = results
--TODO switch back to API call
- --br.getDistanceToLocation(player.unit, summonJadeSerpentStatuePosition.x, summonJadeSerpentStatuePosition.y, summonJadeSerpentStatuePosition.z)
+ --br.functions.range:getDistanceToLocation(player.unit, summonJadeSerpentStatuePosition.x, summonJadeSerpentStatuePosition.y, summonJadeSerpentStatuePosition.z)
if distanceToStatue > 40 or totemInfo.jadeSerpentStatueDuration <= 5 then
tx = tx + math.random(-2, 2)
ty = ty + math.random(-2, 2)
- if br.castGroundAtLocation({ x = tx, y = ty, z = tz }, spell.summonJadeSerpentStatue) then
- br.addonDebug(colors.yellow ..
+ if br.engines.healingEngineFunctions:castGroundAtLocation({ x = tx, y = ty, z = tz }, spell.summonJadeSerpentStatue) then
+ br.functions.misc:addonDebug(colors.yellow ..
"[?]:5P | Jade Serpent Statue - distance to statue: " ..
distanceToStatue .. ", old totem duration: " .. totemInfo.jadeSerpentStatueDuration)
return true
@@ -1379,20 +1379,20 @@ local actionList = {
end
end
-- Arcane Torrent
- if ui.checked(text.utility.arcaneTorrent) and player.race == "BloodElf" and br.getSpellCD(129597) == 0 then
+ if ui.checked(text.utility.arcaneTorrent) and player.race == "BloodElf" and br.functions.spell:getSpellCD(129597) == 0 then
if player.mana <= ui.value(text.utility.arcaneTorrent) then
- br._G.CastSpellByName(GetSpellInfo(129597))
+ br._G.CastSpellByName(br._G.GetSpellInfo(129597))
ui.debug("[?]: " .. text.utility.arcaneTorrent)
return true
end
end
-- Detox
if ui.mode.detox == 1 and cd.detox.ready() then
- for i = 1, #br.friend do
- if br.canDispel(br.friend[i].unit, spell.detox) and (br.getLineOfSight(br.friend[i].unit) and br.getDistance(br.friend[i].unit) <= 40 or br.friend[i].unit == "player") then
- if cast.able.detox(br.friend[i].unit) then
- if cast.detox(br.friend[i].unit) then
- ui.debug(buildSingleActionMessage(br.friend[i], " DETOX"))
+ for i = 1, #br.engines.healingEngine.friend do
+ if br.functions.aura:canDispel(br.engines.healingEngine.friend[i].unit, spell.detox) and (br.functions.misc:getLineOfSight(br.engines.healingEngine.friend[i].unit) and br.functions.range:getDistance(br.engines.healingEngine.friend[i].unit) <= 40 or br.engines.healingEngine.friend[i].unit == "player") then
+ if cast.able.detox(br.engines.healingEngine.friend[i].unit) then
+ if cast.detox(br.engines.healingEngine.friend[i].unit) then
+ ui.debug(buildSingleActionMessage(br.engines.healingEngine.friend[i], " DETOX"))
return true
end
end
@@ -1434,12 +1434,12 @@ local actionList = {
--Summon Jade Serpent Statue
if ui.checked(text2.utility2.summonJadeSerpentStatue2) and talent.summonJadeSerpentStatue and cd.summonJadeSerpentStatue.ready() then
local aroundUnit
- if ui.value(text2.utility2.summonJadeSerpentStatue2) == 1 and #br.friend > 1 then
+ if ui.value(text2.utility2.summonJadeSerpentStatue2) == 1 and #br.engines.healingEngine.friend > 1 then
aroundUnit = tanks[1].unit
else
aroundUnit = player.unit
end
- local tx, ty, tz = br.GetObjectPosition(aroundUnit)
+ local tx, ty, tz = br.functions.unit:GetObjectPosition(aroundUnit)
local results = sqrt(
((tx - summonJadeSerpentStatuePosition.x) ^ 2) +
((ty - summonJadeSerpentStatuePosition.y) ^ 2) +
@@ -1447,12 +1447,12 @@ local actionList = {
)
local distanceToStatue = results
--TODO switch back to API call
- --br.getDistanceToLocation(player.unit, summonJadeSerpentStatuePosition.x, summonJadeSerpentStatuePosition.y, summonJadeSerpentStatuePosition.z)
+ --br.functions.range:getDistanceToLocation(player.unit, summonJadeSerpentStatuePosition.x, summonJadeSerpentStatuePosition.y, summonJadeSerpentStatuePosition.z)
if distanceToStatue > 40 or totemInfo.jadeSerpentStatueDuration <= 5 then
tx = tx + math.random(-2, 2)
ty = ty + math.random(-2, 2)
- if br.castGroundAtLocation({ x = tx, y = ty, z = tz }, spell.summonJadeSerpentStatue) then
- br.addonDebug(colors.yellow ..
+ if br.engines.healingEngineFunctions:castGroundAtLocation({ x = tx, y = ty, z = tz }, spell.summonJadeSerpentStatue) then
+ br.functions.misc:addonDebug(colors.yellow ..
"[?]:20P | Jade Serpent Statue - distance to statue: " ..
distanceToStatue .. ", old totem duration: " .. totemInfo.jadeSerpentStatueDuration)
return true
@@ -1460,18 +1460,18 @@ local actionList = {
end
end
-- Arcane Torrent
- if ui.checked(text2.utility2.arcaneTorren2t) and player.race == "BloodElf" and br.getSpellCD(129597) == 0 then
+ if ui.checked(text2.utility2.arcaneTorren2t) and player.race == "BloodElf" and br.functions.spell:getSpellCD(129597) == 0 then
if player.mana <= ui.value(text2.utility2.arcaneTorrent2) then
- br._G.CastSpellByName(GetSpellInfo(129597))
+ br._G.CastSpellByName(br._G.GetSpellInfo(129597))
ui.debug("[?]: " .. text2.utility2.arcaneTorrent2)
return true
end
end
-- Detox
if ui.mode.detox == 1 and cd.detox.ready() then
- for i = 1, #br.friend do
- if br.canDispel(br.friend[i].unit, spell.detox) and (br.getLineOfSight(br.friend[i].unit) and br.getDistance(br.friend[i].unit) <= 40 or br.friend[i].unit == "player") then
- if cast.detox(br.friend[i].unit) then
+ for i = 1, #br.engines.healingEngine.friend do
+ if br.functions.aura:canDispel(br.engines.healingEngine.friend[i].unit, spell.detox) and (br.functions.misc:getLineOfSight(br.engines.healingEngine.friend[i].unit) and br.functions.range:getDistance(br.engines.healingEngine.friend[i].unit) <= 40 or br.engines.healingEngine.friend[i].unit == "player") then
+ if cast.detox(br.engines.healingEngine.friend[i].unit) then
return true
end
end
@@ -1756,7 +1756,7 @@ local actionList = {
for i = 1, #enemies.range5 do
local thisUnit = enemies.range5[i]
if br._G.ObjectIsFacing(player.unit, thisUnit) then
- if touchOfDeathMode == 1 or (touchOfDeathMode == 2 and br.isBoss(thisUnit)) then
+ if touchOfDeathMode == 1 or (touchOfDeathMode == 2 and br.functions.unit:isBoss(thisUnit)) then
if cast.able.touchOfDeath(thisUnit) then
if cast.touchOfDeath(thisUnit) then
ui.debug("[SUCCESS]: " .. text.damage.touchOfDeath)
@@ -1773,8 +1773,8 @@ local actionList = {
for i = 1, #enemies.range5 do
local thisUnit = enemies.range5[i]
if br._G.ObjectIsFacing(player.unit, thisUnit) then
- if touchOfDeathMode == 1 or (touchOfDeathMode == 2 and br.isBoss(thisUnit)) then
- if unit.health(player.unit) > unit.health(thisUnit) or (br.isBoss(thisUnit) and unit.hp(thisUnit) <= 15) and cast.able.touchofDeath(thisUnit) then
+ if touchOfDeathMode == 1 or (touchOfDeathMode == 2 and br.functions.unit:isBoss(thisUnit)) then
+ if unit.health(player.unit) > unit.health(thisUnit) or (br.functions.unit:isBoss(thisUnit) and unit.hp(thisUnit) <= 15) and cast.able.touchofDeath(thisUnit) then
if cast.touchOfDeath(thisUnit) then
ui.debug("[SUCCESS]: " .. text2.damage2.touchOfDeath2)
return true
@@ -2087,7 +2087,7 @@ local getTotemInfo = function()
for index = 1, 5 do
local exists, totemName, startTime, duration, icon = GetTotemInfo(index)
if exists then
- local estimateDuration = br.round2(startTime + duration - GetTime())
+ local estimateDuration = br.functions.misc:round2(startTime + duration - GetTime())
if icon == 620831 then
totemInfo.jadeSerpentStatueDuration = estimateDuration
var.jadeSerpentStatueName = totemName
@@ -2120,49 +2120,49 @@ local function runRotation()
--fix wonky HE friends list
--TODO fix in HE and add defecit
- for i = 1, #br.friend do
- br.friend[i].hpDefecit = getHPDefecit(br.friend[i].unit)
- br.friend[i].hp = getRealHP(br.friend[i].unit)
+ for i = 1, #br.engines.healingEngine.friend do
+ br.engines.healingEngine.friend[i].hpDefecit = getHPDefecit(br.engines.healingEngine.friend[i].unit)
+ br.engines.healingEngine.friend[i].hp = getRealHP(br.engines.healingEngine.friend[i].unit)
end
- table.sort(br.friend, function(f1, f2)
+ table.sort(br.engines.healingEngine.friend, function(f1, f2)
if f1 == nil then return false end
if f2 == nil then return true end
return f1.hpDefecit > f2.hpDefecit
end)
friends = {
- lowest = br.friend[1],
- range10 = br.getAllies("player", 10),
- range30 = br.getAllies("player", 30),
- range40 = br.getAllies("player", 40),
+ lowest = br.engines.healingEngine.friend[1],
+ range10 = br.engines.healingEngineFunctions:getAllies("player", 10),
+ range30 = br.engines.healingEngineFunctions:getAllies("player", 30),
+ range40 = br.engines.healingEngineFunctions:getAllies("player", 40),
}
if br.player.ui.mode.content == 1 then
friends.lowAllies = {
- essenceFont = br.getLowAlliesInTable(br.player.ui.value(text.heal.essenceFont .. "2"),
+ essenceFont = br.functions.combat:getLowAlliesInTable(br.player.ui.value(text.heal.essenceFont .. "2"),
friends.range30),
- essenceFontOoc = br.getLowAlliesInTable(
+ essenceFontOoc = br.functions.combat:getLowAlliesInTable(
br.player.ui.value(text.heal.outOfCombat.essenceFont .. "2"), friends.range30),
- revival = br.getLowAlliesInTable(br.player.ui.value(text.heal.revival .. "2"),
+ revival = br.functions.combat:getLowAlliesInTable(br.player.ui.value(text.heal.revival .. "2"),
friends.range40),
- refreshingJadeWind = br.getLowAlliesInTable(br.player.ui.value(text.heal.refreshingJadeWind .. "2"),
+ refreshingJadeWind = br.functions.combat:getLowAlliesInTable(br.player.ui.value(text.heal.refreshingJadeWind .. "2"),
friends.range10),
- invokeYulonTheJadeSerpent = br.getLowAlliesInTable(
+ invokeYulonTheJadeSerpent = br.functions.combat:getLowAlliesInTable(
br.player.ui.value(text.heal.invokeYulonTheJadeSerpent .. "2"), friends.range40),
- invokeChiJiTheRedCrane = br.getLowAlliesInTable(br.player.ui.value(text.heal.invokeChiJiTheRedCrane .. "2"),
+ invokeChiJiTheRedCrane = br.functions.combat:getLowAlliesInTable(br.player.ui.value(text.heal.invokeChiJiTheRedCrane .. "2"),
friends.range40),
}
elseif br.player.ui.mode.content == 2 then
friends.lowAllies = {
- essenceFont = br.getLowAlliesInTable(br.player.ui.value(text2.heal2.essenceFont2 .. "2"),
+ essenceFont = br.functions.combat:getLowAlliesInTable(br.player.ui.value(text2.heal2.essenceFont2 .. "2"),
friends.range30),
- essenceFontOoc = br.getLowAlliesInTable(
+ essenceFontOoc = br.functions.combat:getLowAlliesInTable(
br.player.ui.value(text2.heal2.outOfCombat2.essenceFont2 .. "2"), friends.range30),
- revival = br.getLowAlliesInTable(br.player.ui.value(text2.heal2.revival2 .. "2"),
+ revival = br.functions.combat:getLowAlliesInTable(br.player.ui.value(text2.heal2.revival2 .. "2"),
friends.range40),
- refreshingJadeWind = br.getLowAlliesInTable(br.player.ui.value(text2.heal2.refreshingJadeWind2 .. "2"),
+ refreshingJadeWind = br.functions.combat:getLowAlliesInTable(br.player.ui.value(text2.heal2.refreshingJadeWind2 .. "2"),
friends.range10),
- invokeYulonTheJadeSerpent = br.getLowAlliesInTable(
+ invokeYulonTheJadeSerpent = br.functions.combat:getLowAlliesInTable(
br.player.ui.value(text2.heal2.invokeYulonTheJadeSerpent2 .. "2"), friends.range40),
- invokeChiJiTheRedCrane = br.getLowAlliesInTable(
+ invokeChiJiTheRedCrane = br.functions.combat:getLowAlliesInTable(
br.player.ui.value(text2.heal2.invokeChiJiTheRedCrane2 .. "2"), friends.range40),
}
end
@@ -2173,13 +2173,13 @@ local function runRotation()
race = br.player.unit.race(),
isFlying = IsFlying(),
isMounted = IsMounted(),
- isMoving = br.isMoving("player"),
- isDrinking = br.getBuffRemain("player", 308433) > 0 or br.getBuffRemain("player", 167152) > 0,
+ isMoving = br.functions.misc:isMoving("player"),
+ isDrinking = br.functions.aura:getBuffRemain("player", 308433) > 0 or br.functions.aura:getBuffRemain("player", 167152) > 0,
inCombat = br.player.inCombat
}
spell = br.player.spells
talent = br.player.talent
- tanks = br.getTanksTable()
+ tanks = br.engines.healingEngineFunctions:getTanksTable()
totemInfo = {
jadeSerpentStatueDuration = 0,
yulonDuration = 0,
@@ -2188,12 +2188,12 @@ local function runRotation()
ui = br.player.ui
unit = br.player.unit
var = br.player.variables
- ui.mode.thunderFocusTea = br.data.settings[br.selectedSpec].toggles["ThunderFocusTea"]
- ui.mode.dps = br.data.settings[br.selectedSpec].toggles["DPS"]
- ui.mode.RollMode = br.data.settings[br.selectedSpec].toggles["RollMode"]
+ ui.mode.thunderFocusTea = br.data.settings[br.loader.selectedSpec].toggles["ThunderFocusTea"]
+ ui.mode.dps = br.data.settings[br.loader.selectedSpec].toggles["DPS"]
+ ui.mode.RollMode = br.data.settings[br.loader.selectedSpec].toggles["RollMode"]
var.jadeSerpentStatueName = ""
getTotemInfo()
- if br.pause(true) or player.isMounted or player.isFlying or player.isDrinking then return true; end
+ if br.functions.misc:pause(true) or player.isMounted or player.isFlying or player.isDrinking then return true; end
if (br.player.ui.mode.content == 1 and ui.checked(text.utility.summonJadeSerpentStatue)) or
(br.player.ui.mode.content == 2 and ui.checked(text2.utility2.summonJadeSerpentStatue2)) then
@@ -2219,9 +2219,9 @@ local function runRotation()
if actionList.Extra() then return true; end
--don't interrupt certain channelings
- if br.isCastingSpell(spell.manaTea) and player.mana <= 90 then return true end;
+ if br.functions.cast:isCastingSpell(spell.manaTea) and player.mana <= 90 then return true end;
- if ui.mode.RollMode == 1 and br.isMoving("player") and cast.able.roll() then
+ if ui.mode.RollMode == 1 and br.functions.misc:isMoving("player") and cast.able.roll() then
if cast.roll() then return true; end
end
@@ -2285,9 +2285,10 @@ local function runRotation()
end
end -- End runRotation
local id = 270 -- Change to the spec id profile is for. Spec ID can be found at: https://wowpedia.fandom.com/wiki/SpecializationID
+local expansion = br.isMOP
-- DO NOT EDIT ANYTHING BELOW THIS LINE, WILL BREAK PROFILE --
-if br.rotations[id] == nil then br.rotations[id] = {} end
-tinsert(br.rotations[id], {
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Monk/WindWalker/BrewWWPVE.lua b/Rotations/Monk/WindWalker/BrewWWPVE.lua
index 942d2a8a8b..9c8e442e0d 100644
--- a/Rotations/Monk/WindWalker/BrewWWPVE.lua
+++ b/Rotations/Monk/WindWalker/BrewWWPVE.lua
@@ -71,15 +71,15 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdown(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdown(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
--Cooldown Key Toggle
- br.ui:createDropdown(section, "Cooldown Mode", br.dropOptions.Toggle, 3)
+ br.ui:createDropdown(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 3)
--Defensive Key Toggle
- br.ui:createDropdown(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupts Key Toggle
- br.ui:createDropdown(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
-- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -110,7 +110,7 @@ local chi
local getRealHP = function(Unit)
if Unit ~= nil then
- if br.GetObjectExists(Unit) and br.GetUnitIsVisible(Unit) and not br.GetUnitIsDeadOrGhost(Unit) then
+ if br.functions.unit:GetObjectExists(Unit) and br.functions.unit:GetUnitIsVisible(Unit) and not br.functions.unit:GetUnitIsDeadOrGhost(Unit) then
return 100 * (br._G.UnitHealth(Unit) + br._G.UnitGetIncomingHeals(Unit, "player")) /
br._G.UnitHealthMax(Unit)
end
@@ -193,11 +193,11 @@ actionList.PreCombat = function()
-- Logic based on pull timers (IE: DBM/BigWigs)
end -- End Pre-Pull
if unit.valid("target") then -- Abilities below this only used when target is valid
- if br.unlocker == "Tinkr" and not br.getFacing("player", "target") then
+ if br.unlockers.selected == "Tinkr" and not br.functions.unit:getFacing("player", "target") then
return br._G.FaceObject("target")
end
- if br.unlocker == "Tinkr" and unit.distance("target") > 5 then
+ if br.unlockers.selected == "Tinkr" and unit.distance("target") > 5 then
return br._G.MoveToRaw(br._G.ObjectRawPosition("target"))
end
@@ -278,7 +278,7 @@ local function runRotation() -- This is the main profile loop, any below this po
------------
--- Main ---
------------
- if br.unlocker == "Tinkr" then
+ if br.unlockers.selected == "Tinkr" then
br._G.FaceObject("target")
end
@@ -385,10 +385,11 @@ local function runRotation() -- This is the main profile loop, any below this po
end -- End In Combat Rotation
end -- Pause
end -- End runRotation
-local id = 269 -- Change to the spec id profile is for. Spec ID can be found at: https://wowpedia.fandom.com/wiki/SpecializationID
+local id = 0 -- Change to the spec id profile is for. Spec ID can be found at: https://wowpedia.fandom.com/wiki/SpecializationID
+local expansion = br.isMOP
-- DO NOT EDIT ANYTHING BELOW THIS LINE, WILL BREAK PROFILE --
-if br.rotations[id] == nil then br.rotations[id] = {} end
-tinsert(br.rotations[id], {
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Monk/WindWalker/BrewWindWalker.lua b/Rotations/Monk/WindWalker/BrewWindWalker.lua
index f8cf178dd9..58a8a936d1 100644
--- a/Rotations/Monk/WindWalker/BrewWindWalker.lua
+++ b/Rotations/Monk/WindWalker/BrewWindWalker.lua
@@ -122,15 +122,15 @@ local function createOptions()
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdown(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdown(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
--Cooldown Key Toggle
- br.ui:createDropdown(section, "Cooldown Mode", br.dropOptions.Toggle, 3)
+ br.ui:createDropdown(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 3)
--Defensive Key Toggle
- br.ui:createDropdown(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupts Key Toggle
- br.ui:createDropdown(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
-- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = {{
@@ -243,7 +243,7 @@ end -- End Action List - Extra
-- Action List - Defensive
actionList.Defensive = function()
- if br.canDispel("player") and cast.able.detox() then
+ if br.functions.aura:canDispel("player") and cast.able.detox() then
if cast.detox() then debugMessage("DETOX") return true end
end
end -- End Action List - Defensive
@@ -614,10 +614,10 @@ local function runRotation() -- This is the main profile loop, any below this po
chiDefecit = chiMax-chi
fight_remains = unit.ttdGroup(40)
- ui.mode.AutoPull = br.data.settings[br.selectedSpec].toggles["Autopull"]
- ui.mode.CoolDown = br.data.settings[br.selectedSpec].toggles["Cooldown"]
- ui.mode.Rotation = br.data.settings[br.selectedSpec].toggles["Rotation"]
- ui.mode.Debug = br.data.settings[br.selectedSpec].toggles["Debugs"]
+ ui.mode.AutoPull = br.data.settings[br.loader.selectedSpec].toggles["Autopull"]
+ ui.mode.CoolDown = br.data.settings[br.loader.selectedSpec].toggles["Cooldown"]
+ ui.mode.Rotation = br.data.settings[br.loader.selectedSpec].toggles["Rotation"]
+ ui.mode.Debug = br.data.settings[br.loader.selectedSpec].toggles["Debugs"]
local validTargets=nil
@@ -678,7 +678,7 @@ var.XuenTTL = 0
for index = 1,5 do
local exists, totemName, startTime, duration, icon = GetTotemInfo(index)
if exists then
- local estimateDuration = br.round2(startTime + duration - GetTime())
+ local estimateDuration = br.functions.misc:round2(startTime + duration - GetTime())
if icon == 4667418 then
var.hasWhiteTigerStatue = true
var.WhiteTigerStatueTTL = estimateDuration
@@ -825,10 +825,11 @@ end
end -- End In Combat Rotation
end -- Pause
end -- End runRotation
-local id = 269 -- Change to the spec id profile is for. Spec ID can be found at: https://wowpedia.fandom.com/wiki/SpecializationID
+local id = 0 -- Change to the spec id profile is for. Spec ID can be found at: https://wowpedia.fandom.com/wiki/SpecializationID
+local expansion = br.isMOP
-- DO NOT EDIT ANYTHING BELOW THIS LINE, WILL BREAK PROFILE --
-if br.rotations[id] == nil then br.rotations[id] = {} end
-tinsert(br.rotations[id],{
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+tinsert(br.loader.rotations[id],{
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Monk/Windwalker/WindwalkerCuteOne.lua b/Rotations/Monk/Windwalker/WindwalkerCuteOne.lua
new file mode 100644
index 0000000000..c09ae437b6
--- /dev/null
+++ b/Rotations/Monk/Windwalker/WindwalkerCuteOne.lua
@@ -0,0 +1,982 @@
+local rotationName = "CuteOne"
+
+---------------
+--- Toggles ---
+---------------
+local function createToggles()
+ -- Rotation Button
+ local RotationModes = {
+ [1] = { mode = "Auto", value = 1, overlay = "Automatic Rotation Enabled", tip = "Swaps between Single and Multiple based on number of targets in range.", highlight = 1, icon = br.player.spells.spinningCraneKick },
+ [2] = { mode = "AOE", value = 2, overlay = "AOE Rotation Enabled", tip = "AOE Rotation Used", highlight = 0, icon = br.player.spells.spinningCraneKick },
+ [3] = { mode = "Single", value = 3, overlay = "Single Target Rotation Enabled", tip = "Single Target Rotation Used", highlight = 0, icon = br.player.spells.tigerPalm },
+ [4] = { mode = "Off", value = 4, overlay = "Rotation Disabled", tip = "No Rotation will be used.", highlight = 0, icon = br.player.spells.expelHarm }
+ };
+ br.ui:createToggle(RotationModes, "Rotation", 1, 0)
+ -- Cooldown Button
+ local CooldownModes = {
+ [1] = { mode = "Auto", value = 1, overlay = "Cooldowns Automated", tip = "Automatic Cooldowns - Boss Detection.", highlight = 1, icon = br.player.spells.touchOfDeath },
+ [2] = { mode = "On", value = 1, overlay = "Cooldowns Enabled", tip = "Cooldowns used regardless of target.", highlight = 0, icon = br.player.spells.touchOfDeath },
+ [3] = { mode = "Off", value = 3, overlay = "Cooldowns Disabled", tip = "No Cooldowns will be used.", highlight = 0, icon = br.player.spells.touchOfDeath }
+ };
+ br.ui:createToggle(CooldownModes, "Cooldown", 2, 0)
+ -- Defensive Button
+ local DefensiveModes = {
+ [1] = { mode = "On", value = 1, overlay = "Defensive Enabled", tip = "Enables Defensive", highlight = 1, icon = br.player.spells.expelHarm },
+ [2] = { mode = "Off", value = 2, overlay = "Defensive Disabled", tip = "Disables Defensive", highlight = 0, icon = br.player.spells.expelHarm }
+ };
+ br.ui:createToggle(DefensiveModes, "Defensive", 3, 0)
+ -- Interrupt Button
+ local InterruptModes = {
+ [1] = { mode = "On", value = 1, overlay = "Interrupt Enabled", tip = "Enables Interrupt", highlight = 1, icon = br.player.spells.legSweep },
+ [2] = { mode = "Off", value = 2, overlay = "Interrupt Disabled", tip = "Interrupt Defensive", highlight = 0, icon = br.player.spells.legSweep }
+ };
+ br.ui:createToggle(InterruptModes, "Interrupt", 4, 0)
+ -- Storm, Earth, and Fire Button
+ local SEFModes = {
+ [1] = { mode = "On", value = 1, overlay = "Storm, Earth, and Fire Enabled", tip = "Enables Storm, Earth, and Fire", highlight = 1, icon = br.player.spells.stormEarthAndFire },
+ [2] = { mode = "Off", value = 2, overlay = "Storm, Earth, and Fire Disabled", tip = "Disables Storm, Earth, and Fire", highlight = 0, icon = br.player.spells.stormEarthAndFire }
+ };
+ br.ui:createToggle(SEFModes, "StormEarthAndFire", 5, 0)
+ -- Flying Serpent Kick Button
+ local FSKModes = {
+ [1] = { mode = "On", value = 1, overlay = "Auto Flying Serpent Kick Enabled", tip = "Enables Flying Serpent Kick", highlight = 1, icon = br.player.spells.flyingSerpentKick },
+ [2] = { mode = "Off", value = 2, overlay = "Auto Flying Serpent Kick Disabled", tip = "Disables Flying Serpent Kick", highlight = 0, icon = br.player.spells.flyingSerpentKick }
+ };
+ br.ui:createToggle(FSKModes, "FlyingSerpentKick", 6, 0)
+ -- Chi Builder
+ local ChiBuilderModes = {
+ [1] = { mode = "On", value = 1, overlay = "Chi Builder Enabled", tip = "Enables Chi Builder", highlight = 1, icon = br.player.spells.expelHarm },
+ [2] = { mode = "Off", value = 2, overlay = "Chi Builder Disabled", tip = "Disables Chi Builder", highlight = 0, icon = br.player.spells.expelHarm }
+ };
+ br.ui:createToggle(ChiBuilderModes, "ChiBuilder", 7, 0)
+end
+
+---------------
+--- OPTIONS ---
+---------------
+local function createOptions()
+ local optionTable
+ local function rotationOptions()
+ local section
+ -----------------------
+ --- GENERAL OPTIONS ---
+ -----------------------
+ section = br.ui:createSection(br.ui.window.profile, "General")
+ -- Dummy DPS Test
+ br.ui:createSpinner(section, "DPS Testing", 5, 5, 60, 5,
+ "|cffFFFFFFSet to desired time for test in minuts. Min: 5 / Max: 60 / Interval: 5")
+ -- Crackling Jade lightning
+ br.ui:createCheckbox(section, "Crackling Jade Lightning")
+ br.ui:createSpinnerWithout(section, "Cancel CJL Range", 10, 5, 40, 5,
+ "|cffFFFFFFCancels Crackling Jade Lightning below this range in yards.")
+ -- Grapple Weapon
+ br.ui:createCheckbox(section, "Grapple Weapon", "|cffFFFFFFAutomatically disarms dangerous melee enemies in PvE.")
+ -- Legacy of the Emperor
+ br.ui:createDropdown(section, "Legacy of the Emperor",
+ { "|cffFFFFFFPlayer", "|cffFFFFFFTarget", "|cffFFFFFFMouseover", "|cffFFFFFFFocus", "|cffFFFFFFGroup" }, 1,
+ "|cffFFFFFFSet how to use Legacy of the Emperor")
+ -- Legacy of the White Tiger
+ br.ui:createDropdown(section, "Legacy of the White Tiger",
+ { "|cffFFFFFFPlayer", "|cffFFFFFFTarget", "|cffFFFFFFMouseover", "|cffFFFFFFFocus", "|cffFFFFFFGroup" }, 1,
+ "|cffFFFFFFSet how to use Legacy of the White Tiger")
+ -- Roll
+ br.ui:createCheckbox(section, "Roll")
+ br.ui:checkSectionState(section)
+ ------------------------
+ --- COOLDOWN OPTIONS ---
+ ------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Cooldown")
+ -- Invoke Xuen the White Tiger
+ br.ui:createDropdownWithout(section, "Invoke Xuen", br.ui.dropOptions.AlwaysCdAoeNever,
+ 3, "|cffFFFFFFWhen to use Invoke Xuen the White Tiger")
+ -- Touch of Death
+ br.ui:createDropdownWithout(section, "Touch of Death", br.ui.dropOptions.AlwaysCdAoeNever, 3, "|cffFFFFFFWhen to use Touch of Death")
+ br.ui:checkSectionState(section)
+ -------------------------
+ --- DEFENSIVE OPTIONS ---
+ -------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Defensive")
+ -- Dampen Harm / Diffuse Magic
+ br.ui:createSpinner(section, "Dampen Harm / Diffuse Magic", 60, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ -- Detox
+ br.ui:createCheckbox(section, "Detox")
+ br.ui:createDropdownWithout(section, "Detox - Target",
+ { "|cff00FF00Player", "|cffFFFF00Target", "|cffFF0000Mouseover" }, 1, "|cffFFFFFFTarget to cast on")
+ -- Disable
+ br.ui:createCheckbox(section, "Disable", "|cffFFFFFFDisables all defensive abilities.")
+ -- Expel Harm
+ br.ui:createSpinner(section, "Expel Harm", 40, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ -- Fortifying Brew
+ br.ui:createSpinner(section, "Fortifying Brew", 30, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ -- Healing Sphere
+ br.ui:createSpinner(section, "Healing Sphere", 50, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ br.ui:createDropdownWithout(section, "Healing Sphere - Target",
+ { "|cff00FF00Self", "|cffFFFF00Lowest" }, 1, "|cffFFFFFFTarget to cast Healing Sphere on")
+ -- Leg Sweep
+ br.ui:createSpinner(section, "Leg Sweep - HP", 50, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ br.ui:createSpinner(section, "Leg Sweep - AoE", 5, 0, 10, 1, "|cffFFFFFFNumber of Units in 5 Yards to Cast At")
+ -- Nimble Brew
+ br.ui:createCheckbox(section, "Nimble Brew")
+ -- Resuscitate
+ br.ui:createCheckbox(section, "Resuscitate")
+ br.ui:createDropdownWithout(section, "Resuscitate - Target", { "|cff00FF00Target", "|cffFF0000Mouseover" }, 1,
+ "|cffFFFFFFTarget to cast on")
+ -- Touch of Karma
+ br.ui:createSpinner(section, "Touch of Karma", 60, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ -- Tiger's Lust
+ br.ui:createCheckbox(section, "Tiger's Lust", "|cffFFFFFFAuto-cast Tiger's Lust to free movement impairment.")
+ br.ui:checkSectionState(section)
+ -------------------------
+ --- INTERRUPT OPTIONS ---
+ -------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Interrupts")
+ -- Leg Sweep
+ br.ui:createCheckbox(section, "Leg Sweep")
+ -- Paralysis
+ br.ui:createCheckbox(section, "Paralysis")
+ -- Spear Hand Strike
+ br.ui:createCheckbox(section, "Spear Hand Strike")
+ -- Interrupt Percentage
+ br.ui:createSpinnerWithout(section, "Interrupt At", 0, 0, 95, 5, "|cffFFBB00Cast Percentage to use at.")
+ br.ui:checkSectionState(section)
+ ----------------------
+ --- TOGGLE OPTIONS ---
+ ----------------------
+ section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
+ -- Single/Multi Toggle
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
+ -- Cooldown Key Toggle
+ br.ui:createDropdownWithout(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 6)
+ -- Defensive Key Toggle
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
+ -- Interrupts Key Toggle
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
+ br.ui:checkSectionState(section)
+ end
+ optionTable = { {
+ [1] = "Rotation Options",
+ [2] = rotationOptions,
+ } }
+ return optionTable
+end
+
+--------------
+--- Locals ---
+--------------
+-- BR API Locals
+local buff
+local cast
+local cd
+local charges
+local chi
+local debuff
+local enemies
+local energy
+local module
+local ui
+local unit
+local units
+local spell
+local talent
+-- Profile Specific Locals
+local actionList = {}
+local var = {}
+var.getFacingDistance = br.functions.range.getFacingDistance
+var.haltProfile = false
+var.profileStop = false
+var.grappleWeaponBlacklist = {} -- Stores NPC IDs that don't have weapons
+var.lastGrappleTarget = nil
+
+-- * Get Buff Unit Option (Generic for all buffs)
+local getBuffUnitOption = function(option, buffObject)
+ local thisTar = ui.value(option)
+ local thisUnit
+ if thisTar == 1 then
+ thisUnit = "player"
+ end
+ if thisTar == 2 then
+ thisUnit = "target"
+ end
+ if thisTar == 3 then
+ thisUnit = "mouseover"
+ end
+ if thisTar == 4 then
+ thisUnit = "focus"
+ end
+ if thisTar == 5 then
+ -- Group mode: Only buff when:
+ -- 1. At least one member needs the buff
+ -- 2. All current party/raid members are alive and in range
+ -- 3. Group size has been stable for at least 3 seconds (prevents buffing when someone just left)
+
+ -- Don't buff if group size changed recently (within 3 seconds)
+ if not br.engines.healingEngine:isGroupStable(3) then
+ return nil
+ end
+
+ local allAliveAndInRange = true
+ local someoneNeedsBuff = false
+ local unitNeedingBuff = nil
+ local currentGroupSize = #br.engines.healingEngine.friend
+
+ if currentGroupSize == 1 then return "player" end
+
+ if currentGroupSize > 1 then
+ -- First pass: Check if all members are alive and in range
+ for i = 1, currentGroupSize do
+ local checkUnit = br.engines.healingEngine.friend[i].unit
+ if unit.deadOrGhost(checkUnit) or unit.distance(checkUnit) >= 40 then
+ allAliveAndInRange = false
+ break
+ end
+ end
+
+ -- Second pass: Check if anyone needs the buff
+ if allAliveAndInRange then
+ for i = 1, currentGroupSize do
+ local nextUnit = br.engines.healingEngine.friend[i].unit
+ if buffObject.refresh(nextUnit) and unit.distance(nextUnit) < 40 then
+ someoneNeedsBuff = true
+ if unitNeedingBuff == nil then
+ unitNeedingBuff = nextUnit
+ end
+ end
+ end
+ end
+ end
+
+ -- Only return a unit if all members are ready AND someone needs the buff
+ if allAliveAndInRange and someoneNeedsBuff then
+ thisUnit = unitNeedingBuff or "player"
+ else
+ return nil
+ end
+ end
+ return thisUnit
+end
+
+--------------------
+--- Action Lists ---
+--------------------
+-- Action List - Extra
+actionList.Extra = function()
+ -- * Crackling Jade Lightning
+ if ui.checked("Crackling Jade Lightning") and not unit.mounted() and not cast.current.cracklingJadeLightning()
+ and not unit.moving() and cast.able.cracklingJadeLightning("target") and unit.valid("target")
+ and var.targetDistance > ui.value("Cancel CJL Range")
+ then
+ if cast.cracklingJadeLightning("target") then
+ ui.debug("Casting Crackling Jade Lightning")
+ return true
+ end
+ end
+ -- * Roll
+ if ui.checked("Roll") and cast.able.roll() and unit.moving()
+ and var.targetDistance > 10 and unit.valid("target")
+ and var.getFacingDistance() < 5 and unit.facing("player", "target", 10)
+ then
+ if cast.roll() then
+ ui.debug("Casting Roll")
+ return true
+ end
+ end
+ -- * Grapple Weapon
+ if ui.checked("Grapple Weapon") and unit.inCombat() and #enemies.yards5 > 0 then
+ for i = 1, #enemies.yards40 do
+ local thisUnit = enemies.yards40[i]
+ if cast.able.grappleWeapon(thisUnit) and unit.exists(thisUnit) and unit.distance(thisUnit) <= 40
+ and not debuff.grappleWeapon.exists(thisUnit)
+ then
+ -- Get NPC ID to check blacklist
+ local npcID = br.functions.unit:GetObjectID(thisUnit)
+ -- Skip if this NPC type is blacklisted (known to not have weapons)
+ if not var.grappleWeaponBlacklist[npcID] then
+ var.lastGrappleTarget = thisUnit
+ if cast.grappleWeapon(thisUnit) then
+ ui.debug("Casting Grapple Weapon on " .. unit.name(thisUnit) .. " [Extra]")
+ return true
+ end
+ end
+ end
+ end
+ end
+ -- * Legacy of the Emperor
+ if ui.checked("Legacy of the Emperor") and not (buff.markOfTheWild.exists() or buff.blessingOfKings.exists()) then
+ var.markUnit = getBuffUnitOption("Legacy of the Emperor", buff.legacyOfTheEmperor)
+ -- Only cast if we have a valid unit (will be nil in Group mode if not all members are ready)
+ if var.markUnit and cast.able.legacyOfTheEmperor(var.markUnit) and buff.legacyOfTheEmperor.refresh(var.markUnit)
+ and not unit.inCombat()
+ and not unit.resting() and unit.distance(var.markUnit) < 40
+ then
+ if cast.legacyOfTheEmperor(var.markUnit) then
+ local targetName = ui.value("Legacy of the Emperor") == 5 and "Party" or unit.name(var.markUnit)
+ ui.debug("Casting Legacy of the Emperor on " .. targetName)
+ return true
+ end
+ end
+ end
+ -- * Legacy of the White Tiger
+ if ui.checked("Legacy of the White Tiger") and not (buff.arcaneBrilliance.exists() or buff.dalaranBrilliance.exists()
+ or buff.leaderOfThePack.exists()) then
+ -- Only cast if we have a valid unit (will be nil in Group mode if not all members are ready)
+ var.whiteTigerUnit = getBuffUnitOption("Legacy of the White Tiger", buff.legacyOfTheWhiteTiger)
+ if var.whiteTigerUnit and cast.able.legacyOfTheWhiteTiger(var.whiteTigerUnit) and buff.legacyOfTheWhiteTiger.refresh(var.whiteTigerUnit)
+ and not unit.inCombat()
+ and not unit.resting() and unit.distance(var.whiteTigerUnit) < 40
+ then
+ if cast.legacyOfTheWhiteTiger(var.whiteTigerUnit) then
+ local targetName = ui.value("Legacy of the White Tiger") == 5 and "Party" or unit.name(var.whiteTigerUnit)
+ ui.debug("Casting Legacy of the White Tiger on " .. targetName)
+ return true
+ end
+ end
+ end
+ -- * Dummy Test
+ if ui.checked("DPS Testing") then
+ if unit.exists("target") then
+ if unit.combatTime() >= (tonumber(ui.value("DPS Testing")) * 60) and unit.isDummy() then
+ unit.stopAttack()
+ unit.clearTarget()
+ ui.print(tonumber(ui.value("DPS Testing")) .. " Minute Dummy Test Concluded - Profile Stopped")
+ var.profileStop = true
+ end
+ end
+ end -- End Dummy Test
+end -- End Action List- Extra
+
+-- Action List - Defensive
+actionList.Defensive = function()
+ if ui.useDefensive() and not unit.mounted() then
+ local opValue
+ local thisUnit
+ -- * Disable
+ if ui.checked("Disable") then
+ for i = 1, #enemies.yards5 do
+ local thisUnit = enemies.yards5[i]
+ if cast.able.disable(thisUnit) and unit.isFleeing(thisUnit) then
+ if cast.disable(thisUnit) then
+ ui.debug("Casting Disable on " .. unit.name(thisUnit) .. " [Defensive]")
+ return true
+ end
+ end
+ end
+ end
+ -- * Nimble Brew
+ if ui.checked("Nimble Brew") and cast.able.nimbleBrew()
+ and cast.noControl.nimbleBrew()
+ then
+ if cast.nimbleBrew() then
+ ui.debug("Casting Nimble Brew [Defensive]")
+ return true
+ end
+ end
+ -- * Tiger's Lust
+ if ui.checked("Tiger's Lust") and talent.tigersLust
+ and cast.able.tigersLust() and cast.noControl.tigersLust()
+ and (not ui.checked("Nimble Brew") or not spell.nimbleBrew.known()
+ or cd.nimbleBrew.remain() > 1 and cd.nimbleBrew.remain() < 119)
+ then
+ if cast.tigersLust() then
+ ui.debug("Casting Tiger's Lust [Defensive]")
+ return true
+ end
+ end
+ -- * Expel Harm
+ if ui.checked("Expel Harm") and cast.able.expelHarm()
+ and unit.hp() <= ui.value("Expel Harm") and energy() >= 40
+ then
+ if cast.expelHarm() then
+ ui.debug("Casting Expel Harm [Defensive]")
+ return true
+ end
+ end
+ -- * Touch of Karma
+ if ui.checked("Touch of Karma") and cast.able.touchOfKarma() and unit.hp() <= ui.value("Touch of Karma") then
+ if cast.touchOfKarma() then
+ ui.debug("Casting Touch of Karma [Defensive]")
+ return true
+ end
+ end
+ -- * Fortifying Brew
+ if ui.checked("Fortifying Brew") and cast.able.fortifyingBrew() and unit.hp() <= ui.value("Fortifying Brew")
+ and not buff.fortifyingBrew.exists()
+ then
+ if cast.fortifyingBrew() then
+ ui.debug("Casting Fortifying Brew [Defensive]")
+ return true
+ end
+ end
+ -- * Leg Sweep
+ if ui.checked("Leg Sweep - HP") and cast.able.legSweep() and unit.hp() <= ui.value("Leg Sweep - HP")
+ and unit.inCombat() and #enemies.yards5 > 0
+ then
+ if cast.legSweep() then
+ ui.debug("Casting Leg Sweep - HP [Defensive]")
+ return true
+ end
+ end
+ if ui.checked("Leg Sweep - AoE") and cast.able.legSweep()
+ and #enemies.yards5 >= ui.value("Leg Sweep - AoE")
+ then
+ if cast.legSweep() then
+ ui.debug("Casting Leg Sweep - AoE [Defensive]")
+ return true
+ end
+ end
+ -- * Dampen Harm
+ if ui.checked("Dampen Harm / Diffuse Magic") and cast.able.dampenHarm() and unit.hp() <= ui.value("Dampen Harm / Diffuse Magic")
+ and not buff.dampenHarm.exists() and unit.inCombat()
+ then
+ if cast.dampenHarm() then
+ ui.debug("Casting Dampen Harm [Defensive]")
+ return true
+ end
+ end
+ -- * Diffuse Magic
+ if ui.checked("Dampen Harm / Diffuse Magic") and cast.able.diffuseMagic() and unit.hp() <= ui.value("Dampen Harm / Diffuse Magic")
+ and not buff.diffuseMagic.exists() and unit.inCombat()
+ then
+ if cast.diffuseMagic() then
+ ui.debug("Casting Diffuse Magic [Defensive]")
+ return true
+ end
+ end
+ -- * Detox
+ if ui.checked("Detox") then
+ opValue = ui.value("Detox - Target")
+ if opValue == 1 then
+ thisUnit = "player"
+ elseif opValue == 2 then
+ thisUnit = "target"
+ elseif opValue == 3 then
+ thisUnit = "mouseover"
+ end
+ if cast.able.detox(thisUnit) and (unit.friend(thisUnit) or unit.player(thisUnit))
+ and cast.dispel.detox(thisUnit)
+ then
+ if cast.detox(thisUnit) then
+ ui.debug("Casting Detox on " .. unit.name(thisUnit) .. "[Defensive]")
+ return true
+ end
+ end
+ end
+ -- * Resuscitate
+ if ui.checked("Resuscitate") and not unit.inCombat() then
+ opValue = ui.value("Resuscitate - Target")
+ if opValue == 1 then
+ thisUnit = "target"
+ elseif opValue == 2 then
+ thisUnit = "mouseover"
+ end
+ if cast.able.resuscitate(thisUnit, "dead") and unit.deadOrGhost(thisUnit)
+ and (unit.friend(thisUnit) and unit.player(thisUnit))
+ then
+ if cast.resuscitate(thisUnit, "dead") then
+ ui.debug("Casting Resuscitate on " .. unit.name(thisUnit) .. "[Defensive]")
+ return true
+ end
+ end
+ end
+ -- * Healing Sphere
+ if ui.checked("Healing Sphere") and energy() >= 40 then
+ local healTarget = "player"
+ local shouldCast = false
+
+ if ui.value("Healing Sphere - Target") == 1 then
+ -- Self only
+ if unit.hp() <= ui.value("Healing Sphere") or (not unit.inCombat() and unit.hp() < 80) then
+ healTarget = "player"
+ shouldCast = true
+ end
+ elseif ui.value("Healing Sphere - Target") == 2 then
+ -- Lowest health
+ local lowestUnit = "player"
+ local lowestHP = unit.hp()
+
+ -- Check party/raid members
+ if #br.engines.healingEngine.friend > 1 then
+ for i = 1, #br.engines.healingEngine.friend do
+ local friendUnit = br.engines.healingEngine.friend[i].unit
+ local friendHP = unit.hp(friendUnit)
+ if friendHP < lowestHP and unit.distance(friendUnit) < 40 then
+ lowestHP = friendHP
+ lowestUnit = friendUnit
+ end
+ end
+ end
+
+ if lowestHP <= ui.value("Healing Sphere") or (not unit.inCombat() and lowestHP < 80) then
+ healTarget = lowestUnit
+ shouldCast = true
+ end
+ end
+
+ if shouldCast and cast.able.healingSphere(healTarget, "ground", 0) then
+ if cast.healingSphere(healTarget, "ground", 0) then
+ ui.debug("Casting Healing Sphere on " .. unit.name(healTarget) .. " [Defensive]")
+ return true
+ end
+ end
+ end
+ end
+end -- End Action List - Defensive
+
+-- Action List - Cooldowns
+actionList.Cooldowns = function()
+ -- Trinket - Non-Specific
+ if unit.exists(units.dyn5) and unit.distance(units.dyn5) < 5 then
+ module.BasicTrinkets()
+ end
+end -- End Action List - Cooldowns
+
+-- Action List - Interrupt
+actionList.Interrupt = function()
+ if ui.useInterrupt() then
+ for i = 1, #enemies.yards5 do
+ local thisUnit = enemies.yards5[i]
+ if unit.interruptable(thisUnit, ui.value("Interrupt At")) and unit.distance(thisUnit) < 5 then
+ -- * Quaking Palm (Pandaren Racial)
+ if ui.checked("Use Racial") and unit.race() == "Pandaren" then
+ module.Racial(nil,thisUnit)
+ end
+ -- * Spear Hand Strike
+ if ui.checked("Spear Hand Strike") and cast.able.spearHandStrike(thisUnit)
+ and not (ui.checked("Use Racial") and unit.race() == "Pandaren" and cast.able.quakingPalm(thisUnit))
+ then
+ if cast.spearHandStrike(thisUnit) then
+ ui.debug("Casting Spear Hand Strike [Interrupt]")
+ return true
+ end
+ end
+ -- * Leg Sweep
+ if ui.checked("Leg Sweep") and cast.able.legSweep(thisUnit)
+ and not (ui.checked("Use Racial") and unit.race() == "Pandaren" and cast.able.quakingPalm(thisUnit))
+ and not (ui.checked("Spear Hand Strike") and cast.able.spearHandStrike(thisUnit))
+ then
+ if cast.legSweep(thisUnit) then
+ ui.debug("Casting Leg Sweep [Interrupt]")
+ return true
+ end
+ end
+ end
+ end
+ if #enemies.yards5 > 0 then
+ for i = 1,#enemies.yards20 do
+ local thisUnit = enemies.yards20[i]
+ if unit.interruptable(thisUnit, ui.value("Interrupt At")) then
+ local thisDistance = unit.distance(thisUnit)
+ -- Only use Paralysis on targets outside melee range OR when melee interrupts are on cooldown
+ local useMeleeInterrupt = thisDistance < 5 and (
+ (ui.checked("Use Racial") and unit.race() == "Pandaren" and cast.able.quakingPalm(thisUnit)) or
+ (ui.checked("Spear Hand Strike") and cast.able.spearHandStrike(thisUnit)) or
+ (ui.checked("Leg Sweep") and cast.able.legSweep(thisUnit))
+ )
+
+ -- * Paralysis
+ if ui.checked("Paralysis") and cast.able.paralysis(thisUnit)
+ and thisDistance < 20 and not useMeleeInterrupt
+ then
+ if cast.paralysis(thisUnit) then
+ ui.debug("Casting Paralysis [Interrupt]")
+ return true
+ end
+ end
+ end
+ end
+ end
+ end -- End Interrupt Check
+end -- End Action List - Interrupt
+
+-- Action List - AOE
+actionList.AOE = function()
+ -- * Rushing Jade Wind
+ -- rushing_jade_wind,if=talent.rushing_jade_wind.enabled
+ if talent.rushingJadeWind and cast.able.rushingJadeWind() then
+ if cast.rushingJadeWind() then
+ ui.debug("Casting Rushing Jade Wind [AOE]")
+ return true
+ end
+ end
+ -- * Zen Sphere
+ -- zen_sphere,cycle_targets=1,if=talent.zen_sphere.enabled&!dot.zen_sphere.ticking
+ if talent.zenSphere then
+ for i = 1, #enemies.yards8 do
+ local thisUnit = enemies.yards8[i]
+ if cast.able.zenSphere(thisUnit) and not debuff.zenSphere.exists(thisUnit) then
+ if cast.zenSphere(thisUnit) then
+ ui.debug("Casting Zen Sphere on " .. unit.name(thisUnit) .. " [AOE]")
+ return true
+ end
+ end
+ end
+ end
+ -- * Chi Wave
+ -- chi_wave,if=talent.chi_wave.enabled
+ if talent.chiWave and cast.able.chiWave(units.dyn40) then
+ if cast.chiWave(units.dyn40) then
+ ui.debug("Casting Chi Wave [AOE]")
+ return true
+ end
+ end
+ -- * Chi Burst
+ -- chi_burst,if=talent.chi_burst.enabled
+ if talent.chiBurst and cast.able.chiBurst(units.dyn40) then
+ if cast.chiBurst(units.dyn40) then
+ ui.debug("Casting Chi Burst [AOE]")
+ return true
+ end
+ end
+ -- * Rising Sun Kick
+ -- rising_sun_kick,if=chi=chi.max
+ if cast.able.risingSunKick() and chi() == chi.max() then
+ if cast.risingSunKick() then
+ ui.debug("Casting Rising Sun Kick [AOE]")
+ return true
+ end
+ end
+ -- * Spinning Crane Kick
+ -- spinning_crane_kick,if=!talent.rushing_jade_wind.enabled
+ if cast.able.spinningCraneKick("player", "aoe", 1, 8) and not talent.rushingJadeWind then
+ if cast.spinningCraneKick("player", "aoe", 1, 8) then
+ ui.debug("Casting Spinning Crane Kick [AOE]")
+ return true
+ end
+ end
+end -- End Action List - AOE
+
+-- Action List - Single Target
+actionList.SingleTarget = function()
+ -- * Rising Sun Kick
+ -- rising_sun_kick
+ if cast.able.risingSunKick() then--and not debuff.risingSunKick.exists(units.dyn5) then
+ if cast.risingSunKick() then
+ ui.debug("Casting Rising Sun Kick [Single Target]")
+ return true
+ end
+ end
+ -- * Fists of Fury
+ -- fists_of_fury,if=buff.energizing_brew.down&energy.time_to_max>4&buff.tiger_power.remains>4
+ if cast.able.fistsOfFury("player","cone",1,90) and unit.standingTime() > 1 and (unit.ttdGroup(5) > 5 or unit.isDummy())
+ and not buff.energizingBrew.exists() and energy.ttm() > 4 and buff.tigerPower.remain() > 4
+ then
+ if cast.fistsOfFury("player","cone",1,90) then
+ ui.debug("Casting Fists of Fury [Single Target]")
+ return true
+ end
+ end
+ -- * Chi Wave
+ -- chi_wave,if=talent.chi_wave.enabled&energy.time_to_max>2
+ if talent.chiWave and cast.able.chiWave(units.dyn40) and energy.ttm() > 2 then
+ if cast.chiWave(units.dyn40) then
+ ui.debug("Casting Chi Wave [Single Target]")
+ return true
+ end
+ end
+ -- * Chi Burst
+ -- chi_burst,if=talent.chi_burst.enabled&energy.time_to_max>2
+ if talent.chiBurst and cast.able.chiBurst(units.dyn40) and energy.ttm() > 2 then
+ if cast.chiBurst(units.dyn40) then
+ ui.debug("Casting Chi Burst [Single Target]")
+ return true
+ end
+ end
+ -- * Zen Sphere
+ -- zen_sphere,cycle_targets=1,if=talent.zen_sphere.enabled&energy.time_to_max>2&!dot.zen_sphere.ticking
+ if talent.zenSphere and cast.able.zenSphere(units.dyn40) and energy.ttm() > 2 and not debuff.zenSphere.exists(units.dyn40) then
+ if cast.zenSphere(units.dyn40) then
+ ui.debug("Casting Zen Sphere [Single Target]")
+ return true
+ end
+ end
+ -- * Blackout Kick
+ -- blackout_kick,if=buff.combo_breaker_bok.react
+ if cast.able.blackoutKick() and buff.comboBreakerBoK.exists() then
+ if cast.blackoutKick() then
+ ui.debug("Casting Blackout Kick - Combo Breaker [Single Target]")
+ return true
+ end
+ end
+ -- * Tiger Palm
+ -- tiger_palm,if=buff.combo_breaker_tp.react&(buff.combo_breaker_tp.remains<=2|energy.time_to_max>=2)
+ if cast.able.tigerPalm() and buff.comboBreakerTP.exists() and (buff.comboBreakerTP.remain() <= 2 or energy.ttm() >= 2) then
+ if cast.tigerPalm() then
+ ui.debug("Casting Tiger Palm - Combo Breaker [Single Target]")
+ return true
+ end
+ end
+ -- * Jab
+ -- jab,if=chi.max-chi>=2
+ if cast.able.jab() and (chi.max() - chi()) >= 2 then
+ if cast.jab() then
+ ui.debug("Casting Jab [Single Target]")
+ return true
+ end
+ end
+ -- * Blackout Kick
+ -- blackout_kick,if=energy+energy.regen*cooldown.rising_sun_kick.remains>=40
+ if cast.able.blackoutKick() and (energy() + energy.regen() * cd.risingSunKick.remain() >= 40) then
+ if cast.blackoutKick() then
+ ui.debug("Casting Blackout Kick [Single Target]")
+ return true
+ end
+ end
+end -- End Action List - Single Target
+
+-- Action List - Pre-Combat
+actionList.PreCombat = function()
+ if not unit.inCombat() and not unit.mounted() then
+ if unit.valid("target") then
+ -- * Expel Harm - Chi Builder
+ if ui.mode.chiBuilder == 1 and cast.able.expelHarm() and energy() >= 40 and unit.hp() <= 80 then
+ if cast.expelHarm() then
+ ui.debug("Casting Expel Harm - Chi Builder [Precombat]")
+ return true
+ end
+ end
+ -- * Auto Attack
+ if cast.able.autoAttack("target") and unit.exists("target") and var.targetDistance < 5 then
+ if cast.autoAttack("target") then
+ ui.debug("Casting Auto Attack [Precombat]")
+ return true
+ end
+ end
+ end
+ end
+end -- End Action List - PreCombat
+
+-- Action List - Combat
+actionList.Combat = function()
+ -- Check for combat
+ if unit.valid("target") and cd.global.remain() == 0 then
+ if unit.exists(units.dyn40) and unit.distance(units.dyn40) < 40 and not buff.spinningCraneKick.exists() then
+ -- * Touch of Death
+ if ui.alwaysCdAoENever("Touch of Death",3,5) then
+ for i = 1, #enemies.yards5 do
+ local thisUnit = enemies.yards5[i]
+ if cast.able.touchOfDeath(thisUnit)
+ and (unit.health(thisUnit) <= unit.healthMax("player") or buff.deathNote.exists())
+ then
+ if cast.touchOfDeath(thisUnit) then
+ ui.debug("Casting Touch of Death - Omae wa mou shindeiru")
+ return true
+ end
+ end
+ end
+ end
+ -- * Auto Attack
+ -- auto_attack
+ if cast.able.autoAttack(units.dyn5) and unit.exists(units.dyn5) and unit.distance(units.dyn5) < 5 then
+ if cast.autoAttack(units.dyn5) then
+ ui.debug("Casting Auto Attack [Combat]")
+ return true
+ end
+ end
+ -- * Chi Sphere
+ -- chi_sphere,if=talent.power_strikes.enabled&buff.chi_sphere.react&chi<4
+ -- TODO: Notify when to collect chi spheres
+ -- * Use Items: Potion
+ -- virmens_bite_potion,if=buff.bloodlust.react|target.time_to_die<=60
+ -- TODO: Potion
+ -- * Use Items: Hands
+ -- use_item,name=gloves_of_the_golden_protector
+ -- TODO: Gloves
+ -- * Racial: Troll
+ -- berserking
+ if unit.race() == "Troll" then
+ module.Racial()
+ end
+ -- * Chi Brew
+ -- chi_brew,if=talent.chi_brew.enabled&chi<=2&(trinket.proc.agility.react|(charges=1&recharge_time<=10)|charges=2|target.time_to_die=2&(trinket.proc.agility.react|trinket.proc.strength.react|buff.tigereye_brew.stack>=15|target.time_to_die<40)&debuff.rising_sun_kick.up&buff.tiger_power.up
+ if cast.able.tigereyeBrew("player") and not buff.tigereyeBrewUse.exists("player","EXACT") and chi() >= 2
+ and (buff.tigereyeBrew.stack() >= 15 or (unit.ttdGroup() < 40 and not unit.isDummy()))
+ and debuff.risingSunKick.exists(units.dyn5) and buff.tigerPower.exists()
+ then
+ if cast.tigereyeBrew("player") then
+ ui.debug("Casting Tigereye Brew - 15 Stacks or TTD < 40 [Combat]")
+ return true
+ end
+ end
+ -- * Energizing Brew
+ -- energizing_brew,if=energy.time_to_max>5
+ if cast.able.energizingBrew() and energy.ttm() > 5 then
+ if cast.energizingBrew() then
+ ui.debug("Casting Energizing Brew [Combat]")
+ return true
+ end
+ end
+ -- * Rising Sun Kick
+ -- rising_sun_kick,if=debuff.rising_sun_kick.down
+ if cast.able.risingSunKick() and not debuff.risingSunKick.exists(units.dyn5) then
+ if cast.risingSunKick() then
+ ui.debug("Casting Rising Sun Kick [Combat]")
+ return true
+ end
+ end
+ -- * Tiger Palm
+ -- tiger_palm,if=buff.tiger_power.down&debuff.rising_sun_kick.remains>1&energy.time_to_max>1
+ if cast.able.tigerPalm() and not buff.tigerPower.exists() and debuff.risingSunKick.remain(units.dyn5) > 1 and energy.ttm() > 1 then
+ if cast.tigerPalm() then
+ ui.debug("Casting Tiger Palm [Combat]")
+ return true
+ end
+ end
+ -- * Invoke Xuen
+ -- invoke_xuen,if=talent.invoke_xuen.enabled
+ if ui.alwaysCdAoENever("Invoke Xuen") and talent.invokeXuenTheWhiteTiger and cast.able.invokeXuenTheWhiteTiger() and var.targetDistance < 5 then
+ if cast.invokeXuenTheWhiteTiger() then
+ ui.debug("Casting Invoke Xuen [Combat]")
+ return true
+ end
+ end
+ -- * Run Action List - AOE
+ -- run_action_list,name=aoe,if=active_enemies>=3
+ if ui.useAOE(8,3) and unit.level() >= 46 then
+ if actionList.AOE() then return true end
+ end
+ -- * Run Action List - Single Target
+ -- run_action_list,name=single_target,if=active_enemies<3
+ if ui.useST(8,3) or unit.level() < 46 then
+ if actionList.SingleTarget() then return true end
+ end
+ end -- End In Combat Rotation
+ end
+end -- End Action List - Combat
+
+----------------
+--- ROTATION ---
+----------------
+-- Event frame for error detection
+var.grappleWeaponErrorFrame = var.grappleWeaponErrorFrame or CreateFrame("Frame")
+
+var.grappleWeaponErrorFrame:RegisterEvent("UI_ERROR_MESSAGE")
+var.grappleWeaponErrorFrame:SetScript("OnEvent", function(self, event, errorType, message)
+ if message and var.lastGrappleTarget and var.grappleWeaponBlacklist then
+ -- Check for weapon-related errors (common messages: "Target has no weapons", "Can't do that")
+ local weaponError = message:lower():find("weapon") or message:lower():find("disarm")
+ if weaponError then
+ local npcID = br.functions.unit:GetObjectID(var.lastGrappleTarget)
+ if npcID and not var.grappleWeaponBlacklist[npcID] then
+ var.grappleWeaponBlacklist[npcID] = true
+ print("|cff8000FFBadRotations|r: Blacklisted " .. (UnitName(var.lastGrappleTarget) or "Unknown") .. " (ID: " .. npcID .. ") - No weapons to grapple")
+ end
+ end
+ var.lastGrappleTarget = nil
+ end
+end)
+
+local function runRotation()
+ ---------------------
+ --- Define Locals ---
+ ---------------------
+ -- BR API Locals
+ buff = br.player.buff
+ cast = br.player.cast
+ cd = br.player.cd
+ charges = br.player.charges
+ chi = br.player.power.chi
+ debuff = br.player.debuff
+ enemies = br.player.enemies
+ energy = br.player.power.energy
+ module = br.player.module
+ ui = br.player.ui
+ unit = br.player.unit
+ units = br.player.units
+ spell = br.player.spell
+ talent = br.player.talent
+ -- General Locals
+ var.haltProfile = (unit.inCombat() and var.profileStop) or unit.mounted() or ui.pause() or ui.mode.rotation == 4
+ -- Dynamic Units
+ -- Units
+ units.get(5)
+ units.get(40)
+ -- Enemies
+ enemies.get(5)
+ enemies.get(8)
+ -- enemies.get(8,"player",true) -- No Combat
+ enemies.get(20)
+ enemies.get(40)
+
+ var.targetDistance = unit.distance("target")
+
+ -- Cancel Crackling Jade Lightning
+ if cast.current.cracklingJadeLightning() and var.targetDistance < ui.value("Cancel CJL Range") then
+ if cast.cancel.cracklingJadeLightning() then
+ ui.debug("Canceling Crackling Jade Lightning [Within " .. ui.value("Cancel CJL Range") .. "yrds]")
+ return true
+ end
+ end
+
+
+ ---------------------
+ --- Begin Profile ---
+ ---------------------
+ -- Profile Stop | Pause
+ if not unit.inCombat() and not unit.exists("target") and var.profileStop then
+ var.profileStop = false
+ elseif var.haltProfile then
+ return true
+ else
+ ---------------------------------
+ --- Out Of Combat - Rotations ---
+ ---------------------------------
+ -------------
+ --- Extra ---
+ -------------
+ if actionList.Extra() then return true end
+ -----------------
+ --- Defensive ---
+ -----------------
+ if actionList.Defensive() then return true end
+ if ui.mode.rotation ~= 4 then
+ ------------------------------
+ --- Out of Combat Rotation ---
+ ------------------------------
+ if actionList.PreCombat() then return true end
+ --------------------------
+ --- In Combat Rotation ---
+ --------------------------
+ -----------------
+ --- Interrupt ---
+ -----------------
+ if actionList.Interrupt() then return true end
+ if actionList.Combat() then return true end
+ end
+ end -- Pause
+end -- End runRotation
+local id = 269
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
+ name = rotationName,
+ toggles = createToggles,
+ options = createOptions,
+ run = runRotation,
+})
diff --git a/Rotations/Paladin/Initial/InitialPaladin-MOP.lua b/Rotations/Paladin/Initial/InitialPaladin-MOP.lua
new file mode 100644
index 0000000000..a47988eea4
--- /dev/null
+++ b/Rotations/Paladin/Initial/InitialPaladin-MOP.lua
@@ -0,0 +1,302 @@
+local rotationName = "Initial"
+
+---------------
+--- Toggles ---
+---------------
+local function createToggles()
+ -- Rotation Button
+ local RotationModes = {
+ [1] = { mode = "On", value = 1, overlay = "Rotation Enabled", tip = "Enables Rotation", highlight = 1, icon = br.player.spells.crusaderStrike },
+ [2] = { mode = "Off", value = 2, overlay = "Rotation Disabled", tip = "Disables Rotation", highlight = 0, icon = br.player.spells.crusaderStrike }
+ };
+ br.ui:createToggle(RotationModes, "Rotation", 1, 0)
+ -- Defensive Button
+ local DefensiveModes = {
+ [1] = { mode = "On", value = 1, overlay = "Defensive Enabled", tip = "Enables Defensive", highlight = 1, icon = br.player.spells.flashOfLight },
+ [2] = { mode = "Off", value = 2, overlay = "Defensive Disabled", tip = "Disables Defensive", highlight = 0, icon = br.player.spells.flashOfLight }
+ };
+ br.ui:createToggle(DefensiveModes, "Defensive", 2, 0)
+ -- Interrupt Button
+ local InterruptModes = {
+ [1] = { mode = "On", value = 1, overlay = "Interrupt Enabled", tip = "Enables Interrupt", highlight = 1, icon = br.player.spells.hammerOfJustice },
+ [2] = { mode = "Off", value = 2, overlay = "Interrupt Disabled", tip = "Disables Interrupt", highlight = 0, icon = br.player.spells.hammerOfJustice }
+ };
+ br.ui:createToggle(InterruptModes, "Interrupt", 3, 0)
+end
+
+---------------
+--- OPTIONS ---
+---------------
+local function createOptions()
+ local optionTable
+ local function rotationOptions()
+ local section
+ -----------------------
+ --- GENERAL OPTIONS ---
+ -----------------------
+ section = br.ui:createSection(br.ui.window.profile, "General")
+ -- Seal of Command
+ br.ui:createCheckbox(section, "Seal of Command")
+ br.ui:checkSectionState(section)
+ ------------------------
+ --- COOLDOWN OPTIONS ---
+ ------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Cooldown")
+
+ br.ui:checkSectionState(section)
+ -------------------------
+ --- DEFENSIVE OPTIONS ---
+ -------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Defensive")
+ -- Hammer of Justice
+ br.ui:createSpinner(section, "Hammer of Justice - Defensive", 30, 0, 100, 5,
+ "|cffFFFFFFHealth Percent to Cast At")
+ -- Word of Glory
+ br.ui:createSpinner(section, "Word of Glory", 40, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ br.ui:checkSectionState(section)
+ -------------------------
+ --- INTERRUPT OPTIONS ---
+ -------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Interrupts")
+ -- Hammer of Justice
+ br.ui:createCheckbox(section, "Hammer of Justice")
+ -- Interrupt Percentage
+ br.ui:createSpinnerWithout(section, "Interrupt At", 0, 0, 95, 5, "|cffFFBB00Cast Percentage to use at.")
+ br.ui:checkSectionState(section)
+ ----------------------
+ --- TOGGLE OPTIONS ---
+ ----------------------
+ section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
+ -- Single/Multi Toggle
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
+ --Cooldown Key Toggle
+ br.ui:createDropdownWithout(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 6)
+ --Defensive Key Toggle
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
+ -- Interrupts Key Toggle
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
+ br.ui:checkSectionState(section)
+ end
+ optionTable = { {
+ [1] = "Rotation Options",
+ [2] = rotationOptions,
+ } }
+ return optionTable
+end
+
+--------------
+--- Locals ---
+--------------
+-- BR API Locals
+local buff
+local cast
+local cd
+local debuff
+local enemies
+local module
+local spells
+local ui
+local unit
+local units
+-- Profile Specific Locals
+local actionList = {}
+local var = {}
+var.haltProfile = false
+var.profileStop = false
+
+--------------------
+--- Action Lists ---
+--------------------
+
+-- Action List - Extras
+actionList.Extras = function()
+ -- Seal of Command
+ if ui.checked("Seal of Command") and cast.able.sealOfCommand("player") and not unit.formActive(spells.sealOfCommand) then
+ if cast.sealOfCommand("player") then
+ ui.debug("Casting Seal of Command")
+ return true
+ end
+ end
+end -- End Action List - Extras
+
+-- Action List - Defensive
+actionList.Defensive = function()
+ if ui.useDefensive() then
+ -- Hammer of Justice
+ if ui.checked("Hammer of Justice - Defensive") and cast.able.hammerOfJustice()
+ and unit.inCombat() and unit.hp() < ui.value("Hammer of Justice - Defensive")
+ then
+ if cast.hammerOfJustice() then
+ ui.debug("Casting Hammer of Justice [Defensive]")
+ return true
+ end
+ end
+ -- Word of Glory
+ if ui.checked("Word of Glory") and cast.able.wordOfGlory() then
+ local thisUnit = unit.friend("target") and "target" or "player"
+ if unit.inCombat(thisUnit) and unit.hp(thisUnit) <= ui.value("Word of Glory") then
+ if cast.wordOfGlory(thisUnit) then
+ ui.debug("Casting Word of Glory on " .. unit.name(thisUnit))
+ return true
+ end
+ end
+ end
+ end
+end -- End Action List - Defensive
+
+-- Action List - Interrupt
+actionList.Interrupt = function()
+ if ui.useInterrupt() then
+ for i = 1, #enemies.yards10 do
+ local thisUnit = enemies.yards10[i]
+ if br.functions.spell:canInterrupt(thisUnit, ui.value("Interrupt At")) then
+ -- Hammer of Justice
+ if ui.checked("Hammer of Justice") and cast.able.hammerOfJustice(thisUnit) then
+ if cast.hammerOfJustice(thisUnit) then
+ ui.debug("Casting Hammer of Justice [Interrupt]")
+ return true
+ end
+ end
+ end
+ end
+ end -- End Interrupt Check
+end -- End Action List - Interrupt
+
+-- Action List - Cooldowns
+actionList.Cooldowns = function()
+
+end -- End Action List - Cooldowns
+
+-- Action List - Pre-Combat
+actionList.PreCombat = function()
+ if not unit.inCombat() and not unit.mounted() then
+ if unit.valid("target") then
+ -- Judgment
+ if cast.able.judgment("target") and unit.exists("target") and unit.distance("target") < 30 then
+ if cast.judgment("target") then
+ ui.debug("Casting Judgment [Precombat]")
+ return true
+ end
+ end
+ -- Crusader Strike
+ if cast.able.crusaderStrike("target") and unit.exists("target") and unit.distance("target") < 5 then
+ if cast.crusaderStrike("target") then
+ ui.debug("Casting Crusader Strike [Precombat]")
+ return true
+ end
+ end
+ -- Auto Attack
+ if cast.able.autoAttack("target") and unit.exists("target") and unit.distance("target") < 5 then
+ if cast.autoAttack("target") then
+ ui.debug("Casting Auto Attack [Precombat]")
+ return true
+ end
+ end
+ end
+ end
+end -- End Action List - PreCombat
+
+-- Action List - Combat
+actionList.Combat = function()
+ if unit.valid("target") and cd.global.remain() == 0 then
+ if unit.exists(units.dyn5) and unit.distance(units.dyn5) < 5 then
+ -- Auto Attack
+ if cast.able.autoAttack(units.dyn5) and unit.exists(units.dyn5) and unit.distance(units.dyn5) < 5 then
+ if cast.autoAttack(units.dyn5) then
+ ui.debug("Casting Auto Attack")
+ return true
+ end
+ end
+ -- Judgment
+ if cast.able.judgment() then
+ if cast.judgment() then
+ ui.debug("Casting Judgment")
+ return true
+ end
+ end
+ -- Crusader Strike
+ if cast.able.crusaderStrike() then
+ if cast.crusaderStrike() then
+ ui.debug("Casting Crusader Strike")
+ return true
+ end
+ end
+ end -- End In Combat Rotation
+ end
+end -- End Action List - Combat
+
+----------------
+--- ROTATION ---
+----------------
+local function runRotation()
+ ---------------------
+ --- Define Locals ---
+ ---------------------
+ -- BR API Locals
+ buff = br.player.buff
+ cast = br.player.cast
+ cd = br.player.cd
+ debuff = br.player.debuff
+ enemies = br.player.enemies
+ module = br.player.module
+ spells = br.player.spells
+ ui = br.player.ui
+ unit = br.player.unit
+ units = br.player.units
+ -- Units
+ units.get(5)
+ -- Enemies
+ enemies.get(10)
+ -- General Locals
+ var.haltProfile = (unit.inCombat() and var.profileStop) or unit.mounted() or br.functions.misc:pause() or ui.mode.rotation == 2
+
+
+ ---------------------
+ --- Begin Profile ---
+ ---------------------
+ -- Profile Stop | Pause
+ if not unit.inCombat() and not unit.exists("target") and var.profileStop then
+ var.profileStop = false
+ elseif var.haltProfile then
+ return true
+ else
+ ---------------------------------
+ --- Out Of Combat - Rotations ---
+ ---------------------------------
+ -------------
+ --- Extra ---
+ -------------
+ if actionList.Extras() then return true end
+ -----------------
+ --- Defensive ---
+ -----------------
+ if actionList.Defensive() then return true end
+ ------------------
+ --- Pre-Combat ---
+ ------------------
+ if actionList.PreCombat() then return true end
+ if unit.inCombat() then
+ -----------------
+ --- Cooldowns ---
+ -----------------
+ if actionList.Cooldowns() then return true end
+ -----------------
+ --- Interrupt ---
+ -----------------
+ if actionList.Interrupt() then return true end
+ --------------
+ --- Combat ---
+ --------------
+ if actionList.Combat() then return true end
+ end
+ end -- Pause
+end -- End runRotation
+local id = 1451
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
+ name = rotationName,
+ toggles = createToggles,
+ options = createOptions,
+ run = runRotation,
+})
diff --git a/Rotations/Paladin/Initial/InitialPaladin.lua b/Rotations/Paladin/Initial/InitialPaladin.lua
index 3260c01133..58fb904694 100644
--- a/Rotations/Paladin/Initial/InitialPaladin.lua
+++ b/Rotations/Paladin/Initial/InitialPaladin.lua
@@ -76,15 +76,15 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
--Cooldown Key Toggle
- br.ui:createDropdownWithout(section, "Cooldown Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 6)
--Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupts Key Toggle
- br.ui:createDropdownWithout(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
-- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -181,7 +181,7 @@ actionList.Interrupt = function()
if ui.useInterrupt() then
for i = 1, #enemies.yards10 do
local thisUnit = enemies.yards10[i]
- if br.canInterrupt(thisUnit, ui.value("Interrupt At")) then
+ if br.functions.spell:canInterrupt(thisUnit, ui.value("Interrupt At")) then
-- Hammer of Justice
if ui.checked("Hammer of Justice") and cast.able.hammerOfJustice(thisUnit) then
if cast.hammerOfJustice(thisUnit) then
@@ -289,7 +289,7 @@ local function runRotation()
-- Enemies
enemies.get(10)
-- General Locals
- var.haltProfile = (unit.inCombat() and var.profileStop) or unit.mounted() or br.pause() or ui.mode.rotation == 2
+ var.haltProfile = (unit.inCombat() and var.profileStop) or unit.mounted() or br.functions.misc:pause() or ui.mode.rotation == 2
---------------------
@@ -329,8 +329,9 @@ local function runRotation()
end -- Pause
end -- End runRotation
local id = 1451
-if br.rotations[id] == nil then br.rotations[id] = {} end
-br._G.tinsert(br.rotations[id], {
+local expansion = br.isRetail
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Paladin/Retribution/RetributionCuteOne-MOP.lua b/Rotations/Paladin/Retribution/RetributionCuteOne-MOP.lua
new file mode 100644
index 0000000000..342254bd7f
--- /dev/null
+++ b/Rotations/Paladin/Retribution/RetributionCuteOne-MOP.lua
@@ -0,0 +1,762 @@
+local rotationName = "CuteOne"
+
+---------------
+--- Toggles ---
+---------------
+local function createToggles()
+ -- Rotation Button
+ local RotationModes = {
+ [1] = { mode = "Auto", value = 1, overlay = "Automatic Rotation", tip = "Swaps between Single and Multiple based on number of targets in range.", highlight = 1, icon = br.player.spells.divineStorm },
+ [2] = { mode = "Mult", value = 2, overlay = "Multiple Target Rotation", tip = "Multiple target rotation used.", highlight = 0, icon = br.player.spells.divineStorm },
+ [3] = { mode = "Sing", value = 3, overlay = "Single Target Rotation", tip = "Single target rotation used.", highlight = 0, icon = br.player.spells.crusaderStrike },
+ [4] = { mode = "Off", value = 4, overlay = "DPS Rotation Disabled", tip = "Disable DPS Rotation", highlight = 0, icon = br.player.spells.flashOfLight }
+ }
+ br.ui:createToggle(RotationModes, "Rotation", 1, 0)
+ -- Cooldown Button
+ local CooldownModes = {
+ [1] = { mode = "Auto", value = 1, overlay = "Cooldowns Automated", tip = "Automatic Cooldowns - Boss Detection.", highlight = 1, icon = br.player.spells.avengingWrath },
+ [2] = { mode = "On", value = 1, overlay = "Cooldowns Enabled", tip = "Cooldowns used regardless of target.", highlight = 0, icon = br.player.spells.avengingWrath },
+ [3] = { mode = "Off", value = 3, overlay = "Cooldowns Disabled", tip = "No Cooldowns will be used.", highlight = 0, icon = br.player.spells.avengingWrath }
+ }
+ br.ui:createToggle(CooldownModes, "Cooldown", 2, 0)
+ -- Defensive Button
+ local DefensiveModes = {
+ [1] = { mode = "On", value = 1, overlay = "Defensive Enabled", tip = "Enables Defensive", highlight = 1, icon = br.player.spells.flashOfLight },
+ [2] = { mode = "Off", value = 2, overlay = "Defensive Disabled", tip = "Disables Defensive", highlight = 0, icon = br.player.spells.flashOfLight }
+ };
+ br.ui:createToggle(DefensiveModes, "Defensive", 3, 0)
+ -- Interrupt Button
+ local InterruptModes = {
+ [1] = { mode = "On", value = 1, overlay = "Interrupt Enabled", tip = "Enables Interrupt", highlight = 1, icon = br.player.spells.hammerOfJustice },
+ [2] = { mode = "Off", value = 2, overlay = "Interrupt Disabled", tip = "Disables Interrupt", highlight = 0, icon = br.player.spells.hammerOfJustice }
+ };
+ br.ui:createToggle(InterruptModes, "Interrupt", 4, 0)
+end
+
+---------------
+--- OPTIONS ---
+---------------
+local function createOptions()
+ local optionTable
+ local function rotationOptions()
+ local section
+ local playTarMouseFocLow = { "|cffFFFFFFPlayer", "|cffFFFFFFTarget", "|cffFFFFFFMouseover", "|cffFFFFFFFocus",
+ "|cffFFFFFFLowest" }
+ -----------------------
+ --- GENERAL OPTIONS ---
+ -----------------------
+ section = br.ui:createSection(br.ui.window.profile, "General")
+ -- Dummy DPS Test
+ br.ui:createSpinner(section, "DPS Testing", 5, 5, 60, 5,
+ "|cffFFFFFFSet to desired time for test in minuts. Min: 5 / Max: 60 / Interval: 5")
+ -- Seal of Command
+ br.ui:createCheckbox(section, "Seal of Command/Truth")
+ br.ui:checkSectionState(section)
+ ------------------------
+ --- COOLDOWN OPTIONS ---
+ ------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Cooldown")
+ -- Avenging Wrath
+ br.ui:createDropdownWithout(section, "Avenging Wrath", br.ui.dropOptions.AlwaysCdAoeNever,
+ 2, "|cffFFFFFFSet when to use Avenging Wrath")
+ -- Execution Sentence
+ br.ui:createDropdownWithout(section, "Execution Sentence", br.ui.dropOptions.AlwaysCdAoeNever,
+ 2, "|cffFFFFFFSet when to use Execution Sentence")
+ -- Exorcism
+ br.ui:createCheckbox(section, "Exorcism")
+ -- Guardian of Ancient Kings
+ br.ui:createDropdownWithout(section, "Guardian of Ancient Kings", br.ui.dropOptions.AlwaysCdAoeNever,
+ 2, "|cffFFFFFFSet when to use Guardian of Ancient Kings")
+ -- Holy Avenger
+ br.ui:createDropdownWithout(section, "Holy Avenger", br.ui.dropOptions.AlwaysCdAoeNever,
+ 2, "|cffFFFFFFSet when to use Holy Avenger")
+ -- Holy Prism
+ br.ui:createCheckbox(section, "Holy Prism")
+ -- Light's Hammer
+ br.ui:createDropdownWithout(section, "Light's Hammer", br.ui.dropOptions.AlwaysCdAoeNever,
+ 2, "|cffFFFFFFSet when to use Light's Hammer")
+ br.ui:checkSectionState(section)
+ -------------------------
+ --- DEFENSIVE OPTIONS ---
+ -------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Defensive")
+ -- Cleanse
+ br.ui:createDropdown(section, "Cleanse", playTarMouseFocLow, 1, "|cffFFFFFFTarget to Cast On")
+ -- Divine Shield
+ br.ui:createSpinner(section, "Divine Shield", 35, 0, 100, 5, "|cffFFBB00Health Percentage to use at.")
+ -- Flash of Light
+ br.ui:createSpinner(section, "Flash of Light", 50, 0, 100, 5, "|cffFFBB00Health Percentage to use at.")
+ br.ui:createDropdownWithout(section, "Flash of Light Target", playTarMouseFocLow, 5,
+ "|cffFFFFFFTarget for Flash of Light")
+ -- Hammer of Justice
+ br.ui:createSpinner(section, "Hammer of Justice - Defensive", 30, 0, 100, 5,
+ "|cffFFFFFFHealth Percent to Cast At")
+ -- Lay On Hands
+ br.ui:createSpinner(section, "Lay On Hands", 20, 0, 100, 5, "", "Health Percentage to use at")
+ br.ui:createDropdownWithout(section, "Lay on Hands Target", playTarMouseFocLow, 5,
+ "|cffFFFFFFTarget for Lay On Hands")
+ -- Redemption
+ br.ui:createDropdown(section, "Redemption", { "|cffFFFFFFTarget", "|cffFFFFFFMouseover", "|cffFFFFFFFocus" }, 1,
+ "|cffFFFFFFTarget to Cast On")
+ -- Word of Glory
+ br.ui:createSpinner(section, "Word of Glory", 40, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ br.ui:checkSectionState(section)
+ -------------------------
+ --- INTERRUPT OPTIONS ---
+ -------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Interrupts")
+ -- Hammer of Justice
+ br.ui:createCheckbox(section, "Hammer of Justice")
+ -- Rebuke
+ br.ui:createCheckbox(section, "Rebuke")
+ -- Interrupt Percentage
+ br.ui:createSpinnerWithout(section, "Interrupt At", 0, 0, 95, 5, "|cffFFBB00Cast Percentage to use at.")
+ br.ui:checkSectionState(section)
+ ----------------------
+ --- TOGGLE OPTIONS ---
+ ----------------------
+ section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
+ -- Single/Multi Toggle
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
+ --Cooldown Key Toggle
+ br.ui:createDropdownWithout(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 6)
+ --Defensive Key Toggle
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
+ -- Interrupts Key Toggle
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
+ br.ui:checkSectionState(section)
+ end
+ optionTable = { {
+ [1] = "Rotation Options",
+ [2] = rotationOptions,
+ } }
+ return optionTable
+end
+
+--------------
+--- Locals ---
+--------------
+-- BR API Locals
+local buff
+local cast
+local cd
+local debuff
+local enemies
+local holyPower
+local module
+local spell
+local spells
+local talent
+local ui
+local unit
+local units
+-- Profile Specific Locals
+local actionList = {}
+local var = {}
+var.haltProfile = false
+var.profileStop = false
+
+-----------------
+--- Functions ---
+-----------------
+
+local getHealUnitOption = function(option, checkForbearance)
+ local thisTar = ui.value(option)
+ local thisUnit
+ if thisTar == 1 then
+ thisUnit = "player"
+ end
+ if thisTar == 2 then
+ thisUnit = "target"
+ end
+ if thisTar == 3 then
+ thisUnit = "mouseover"
+ end
+ if thisTar == 4 then
+ thisUnit = "focus"
+ end
+ if thisTar == 5 then
+ thisUnit = var.lowestUnit
+ -- Get the next lowest unit if lowest unit has Forbearance debuff
+ if checkForbearance and #br.engines.healingEngine.friend > 1 and debuff.forbearance.exists(thisUnit) then
+ for i = 1, #br.engines.healingEngine.friend do
+ local nextUnit = br.engines.healingEngine.friend[i].unit
+ if not debuff.forbearance.exists(nextUnit) then
+ thisUnit = nextUnit
+ break
+ end
+ end
+ end
+ end
+ return thisUnit
+end
+
+--------------------
+--- Action Lists ---
+--------------------
+
+-- Action List - Extras
+actionList.Extras = function()
+ -- * Dummy Test
+ if ui.checked("DPS Testing") and unit.isDummy() then
+ if unit.exists("target") then
+ if unit.combatTime() >= (tonumber(ui.value("DPS Testing")) * 60) then
+ unit.stopAttack()
+ unit.clearTarget()
+ ui.print(tonumber(ui.value("DPS Testing")) .. " Minute Dummy Test Concluded - Profile Stopped")
+ var.profileStop = true
+ end
+ end
+ end
+ -- * Seal of Command
+ if ui.checked("Seal of Command/Truth") and cast.able.sealOfCommand("player") and not unit.formActive(spells.sealOfCommand) and not spell.sealOfTruth.known() then
+ if cast.sealOfCommand("player") then
+ ui.debug("Casting Seal of Command")
+ return true
+ end
+ end
+ -- * Seal of Truth
+ if not ui.checked("Seal of Command/Truth") and cast.able.sealOfTruth("player") and not unit.formActive(spells.sealOfTruth) then
+ if cast.sealOfTruth("player") then
+ ui.debug("Casting Seal of Truth")
+ return true
+ end
+ end
+end -- End Action List - Extras
+
+-- Action List - Defensive
+actionList.Defensive = function()
+ if ui.useDefensive() then
+ -- * Cleanse
+ if ui.checked("Cleanse") then
+ var.cleanseUnit = getHealUnitOption("Cleanse")
+ if var.cleanseUnit ~= nil and cast.able.cleanse(var.cleanseUnit) and cast.dispel.cleanse(var.cleanseUnit) then
+ if cast.cleanse(var.cleanseUnit) then
+ ui.debug("Casting Cleanse on " .. unit.name(var.cleanseUnit))
+ return true
+ end
+ end
+ end
+ -- * Divine Shield
+ if ui.checked("Divine Shield") and cast.able.divineShield() and unit.inCombat() then
+ if unit.hp() <= ui.value("Divine Shield") and not debuff.forbearance.exists("player") then
+ if cast.divineShield() then
+ ui.debug("Casting Divine Shield")
+ return true
+ end
+ end
+ end
+ -- * Flash of Light
+ if ui.checked("Flash of Light") and not (unit.mounted() or unit.flying()) and not cast.current.flashOfLight() then
+ var.flashUnit = getHealUnitOption("Flash of Light Target")
+ if var.flashUnit ~= nil and cast.able.flashOfLight(var.flashUnit) and unit.distance(var.flashUnit) < 40 then
+ -- Instant Cast
+ if talent.selflessHealer and buff.selflessHealer.stack() == 4 then
+ -- Don't waste instant heal!
+ var.flashUnit = unit.hp(var.flashUnit) <= ui.value("Flash of Light") and var.flashUnit or
+ var.lowestUnit
+ if cast.flashOfLight(var.flashUnit) then
+ ui.debug("Casting Flash of Light on " .. unit.name(var.flashUnit) .. " [Instant]")
+ return true
+ end
+ end
+ -- Long Cast
+ if not unit.moving("player") and (var.forceHeal
+ or (unit.inCombat() and unit.hp(var.flashUnit) <= ui.value("Flash of Light"))
+ or (not unit.inCombat() and unit.hp(var.flashUnit) <= 90))
+ then
+ if cast.flashOfLight(var.flashUnit) then
+ ui.debug("Casting Flash of Light on " .. unit.name(var.flashUnit) .. " [Long]")
+ return true
+ end
+ end
+ end
+ end
+ -- * Hammer of Justice
+ if ui.checked("Hammer of Justice - Defensive") and cast.able.hammerOfJustice()
+ and unit.inCombat() and unit.hp() < ui.value("Hammer of Justice - Defensive")
+ then
+ if cast.hammerOfJustice() then
+ ui.debug("Casting Hammer of Justice [Defensive]")
+ return true
+ end
+ end
+ -- * Lay On Hands
+ if ui.checked("Lay On Hands") then
+ var.layUnit = getHealUnitOption("Lay On Hands Target", true)
+ if var.layUnit ~= nil and cast.able.layOnHands(var.layUnit) and unit.inCombat(var.layUnit) and not debuff.forbearance.exists(var.layUnit)
+ and unit.hp(var.layUnit) <= ui.value("Lay On Hands") and unit.distance(var.layUnit) < 40
+ then
+ if cast.layOnHands(var.layUnit) then
+ ui.debug("Casting Lay On Hands on " ..
+ tostring(unit.name(var.layUnit)) .. " [" .. tostring(unit.hp(var.layUnit)) .. "% Remaining]")
+ return true
+ end
+ end
+ end
+ -- * Redemption
+ if ui.checked("Redemption") and not unit.moving("player") and var.resable then
+ local redemptionTar = ui.value("Redemption")
+ local redemptionUnit
+ if redemptionTar == 1 then
+ redemptionUnit = "target"
+ end
+ if redemptionTar == 2 then
+ redemptionUnit = "mouseover"
+ end
+ if redemptionTar == 3 then
+ redemptionUnit = "focus"
+ end
+ if redemptionUnit ~= nil and cast.able.redemption(redemptionUnit, "dead") then
+ if cast.redemption(redemptionUnit, "dead") then
+ ui.debug("Casting Redemption on " .. unit.name(redemptionUnit))
+ return true
+ end
+ end
+ end
+ -- * Word of Glory
+ if ui.checked("Word of Glory") and cast.able.wordOfGlory() then
+ local thisUnit = unit.friend("target") and "target" or "player"
+ if unit.inCombat(thisUnit) and unit.hp(thisUnit) <= ui.value("Word of Glory") then
+ if cast.wordOfGlory(thisUnit) then
+ ui.debug("Casting Word of Glory on " .. unit.name(thisUnit))
+ return true
+ end
+ end
+ end
+ end
+end -- End Action List - Defensive
+
+-- Action List - Interrupt
+actionList.Interrupt = function()
+ if ui.useInterrupt() then
+ for i = 1, #enemies.yards10 do
+ local thisUnit = enemies.yards10[i]
+ if br.functions.spell:canInterrupt(thisUnit, ui.value("Interrupt At")) then
+ -- * Hammer of Justice
+ if ui.checked("Hammer of Justice") and cast.able.hammerOfJustice(thisUnit) then
+ if cast.hammerOfJustice(thisUnit) then
+ ui.debug("Casting Hammer of Justice [Interrupt]")
+ return true
+ end
+ end
+ end
+ end
+ for i = 1, #enemies.yards5 do
+ local thisUnit = enemies.yards5[i]
+ if br.functions.spell:canInterrupt(thisUnit, ui.value("Interrupt At")) then
+ -- * Rebuke
+ -- rebuke
+ if ui.checked("Rebuke") and cast.able.rebuke(thisUnit) then
+ if cast.rebuke(thisUnit) then
+ ui.debug("Casting Rebuke [Interrupt]")
+ return true
+ end
+ end
+ end
+ end
+ end -- End Interrupt Check
+end -- End Action List - Interrupt
+
+-- Action List - Cooldowns
+actionList.Cooldowns = function()
+
+end -- End Action List - Cooldowns
+
+-- Action List - Pre-Combat
+actionList.PreCombat = function()
+ if not unit.inCombat() and not unit.mounted() then
+ if unit.valid("target") then
+ -- * Judgment
+ if cast.able.judgment("target") and unit.exists("target") then
+ if cast.judgment("target") then
+ ui.debug("Casting Judgment [Precombat]")
+ return true
+ end
+ end
+ -- * Crusader Strike
+ if cast.able.crusaderStrike("target") and unit.exists("target") then
+ if cast.crusaderStrike("target") then
+ ui.debug("Casting Crusader Strike [Precombat]")
+ return true
+ end
+ end
+ -- * Auto Attack
+ if cast.able.autoAttack("target") and unit.exists("target") then
+ if cast.autoAttack("target") then
+ ui.debug("Casting Auto Attack [Precombat]")
+ return true
+ end
+ end
+ end
+ end
+end -- End Action List - PreCombat
+
+-- Action List - Combat
+actionList.Combat = function()
+ if unit.valid("target") and cd.global.remain() == 0 then
+ if unit.exists(units.dyn5) then
+ -- Potion
+ -- mogu_power_potion,if=(buff.bloodlust.react|(buff.ancient_power.up&buff.avenging_wrath.up)|target.time_to_die<=40)
+ -- TODO: Add Potion Logic Here
+ -- * Auto Attack
+ -- auto_attack
+ if cast.able.autoAttack(units.dyn5) and unit.exists(units.dyn5) and unit.distance(units.dyn5) < 5 then
+ if cast.autoAttack(units.dyn5) then
+ ui.debug("Casting Auto Attack")
+ return true
+ end
+ end
+ -- * Inquisition
+ -- inquisition,if=(buff.inquisition.down|buff.inquisition.remains<=2)&(holy_power>=3|target.time_to_die= 3 or unit.timeToDie(units.dyn5) < holyPower() * 20 or buff.divinePurpose.exists())
+ then
+ if cast.inquisition() then
+ ui.debug("Casting Inquisition")
+ return true
+ end
+ end
+ end
+ -- * Avenging Wrath
+ -- avenging_wrath,if=buff.inquisition.up
+ if ui.alwaysCdAoENever("Avenging Wrath",3,#enemies.yards8) and cast.able.avengingWrath() then
+ if buff.inquisition.exists() then
+ if cast.avengingWrath() then
+ ui.debug("Casting Avenging Wrath")
+ return true
+ end
+ end
+ end
+ -- * Guardian of Ancient Kings
+ -- guardian_of_ancient_kings,if=buff.inquisition.up
+ if ui.alwaysCdAoENever("Guardian of Ancient Kings",3,#enemies.yards8) and cast.able.guardianOfAncientKings() then
+ if buff.inquisition.exists() then
+ if cast.guardianOfAncientKings() then
+ ui.debug("Casting Guardian of Ancient Kings")
+ return true
+ end
+ end
+ end
+ -- * Holy Avenger
+ -- holy_avenger,if=talent.holy_avenger.enabled&(buff.inquisition.up&holy_power<=2)
+ if talent.holyAvenger and ui.alwaysCdAoENever("Holy Avenger",3,#enemies.yards8) and cast.able.holyAvenger() then
+ if buff.inquisition.exists() and holyPower() <= 2 then
+ if cast.holyAvenger() then
+ ui.debug("Casting Holy Avenger")
+ return true
+ end
+ end
+ end
+ -- * Use Item - Reinbinder's Fists
+ -- use_item,name=reinbinders_fists,if=buff.inquisition.up&(buff.ancient_power.down|buff.ancient_power.stack=12)
+ -- TODO: Add Use Item Logic Here
+ -- * Racials
+ -- blood_fury
+ -- berserking
+ -- arcane_torrent
+ if ui.alwaysCdNever("Racial") and cast.able.racial()
+ and (unit.race() == "Orc" or unit.race() == "BloodElf" or unit.race() == "Troll")
+ then
+ if cast.racial() then ui.debug("Casting Racial") return true end
+ end
+ -- * Execution Sentence
+ -- execution_sentence,if=talent.execution_sentence.enabled&(buff.inquisition.up&(buff.ancient_power.down|buff.ancient_power.stack=12))
+ if talent.executionSentence and ui.alwaysCdAoENever("Execution Sentence",3,#enemies.yards8) and cast.able.executionSentence() then
+ if buff.inquisition.exists()
+ and (not buff.ancientPower.exists() or buff.ancientPower.stack() == 12)
+ then
+ if cast.executionSentence() then
+ ui.debug("Casting Execution Sentence")
+ return true
+ end
+ end
+ end
+ -- * Light's Hammer
+ -- lights_hammer,if=talent.lights_hammer.enabled&(buff.inquisition.up&(buff.ancient_power.down|buff.ancient_power.stack=12))
+ if talent.lightsHammer and ui.alwaysCdAoENever("Light's Hammer",3,#enemies.yards8) and cast.able.lightsHammer() then
+ if buff.inquisition.exists()
+ and (not buff.ancientPower.exists() or buff.ancientPower.stack() == 12)
+ then
+ if cast.lightsHammer() then
+ ui.debug("Casting Light's Hammer")
+ return true
+ end
+ end
+ end
+ -- * Divine Storm
+ -- divine_storm,if=active_enemies>=2&(holy_power=5|buff.divine_purpose.react|(buff.holy_avenger.up&holy_power>=3))
+ if ui.useAOE(8,2) and cast.able.divineStorm() then
+ if (holyPower() == 5 or buff.divinePurpose.exists() or (buff.holyAvenger.exists() and holyPower() >= 3)) then
+ if cast.divineStorm() then
+ ui.debug("Casting Divine Storm")
+ return true
+ end
+ end
+ end
+ -- divine_storm,if=buff.divine_crusader.react&holy_power=5
+ if cast.able.divineStorm() then
+ if buff.divineCrusader.exists() and holyPower() == 5 then
+ if cast.divineStorm() then
+ ui.debug("Casting Divine Storm - High Holy Power")
+ return true
+ end
+ end
+ end
+ -- * Templar's Verdict
+ -- templars_verdict,if=holy_power=5|buff.holy_avenger.up&holy_power>=3
+ if (ui.useST(8,2) or not spell.divineStorm.known()) and cast.able.templarsVerdict() then
+ if (holyPower.max() == 3 and holyPower() == 3) or holyPower() == 5 or (buff.holyAvenger.exists() and holyPower() >= 3) then
+ if cast.templarsVerdict() then
+ ui.debug("Casting Templar's Verdict - High Holy Power")
+ return true
+ end
+ end
+ end
+ -- templars_verdict,if=buff.divine_purpose.react&buff.divine_purpose.remains<4
+ if (ui.useST(8,2) or not spell.divineStorm.known()) and cast.able.templarsVerdict() then
+ if buff.divinePurpose.exists() and buff.divinePurpose.remain() < 4 then
+ if cast.templarsVerdict() then
+ ui.debug("Casting Templar's Verdict - Divine Purpose Expires Soon")
+ return true
+ end
+ end
+ end
+ -- * Hammer of Wrath
+ -- hammer_of_wrath
+ if cast.able.hammerOfWrath() then
+ if buff.avengingWrath.exists() or unit.hp(units.dyn5) <= 20 then
+ if cast.hammerOfWrath() then
+ ui.debug("Casting Hammer of Wrath")
+ return true
+ end
+ end
+ end
+ -- * Wait
+ -- wait,sec=cooldown.hammer_of_wrath.remains,if=cooldown.hammer_of_wrath.remains>0&cooldown.hammer_of_wrath.remains<=0.2
+ if cd.hammerOfWrath.remain() > 0 and cd.hammerOfWrath.remain() <= 0.2 then
+ -- ui.debug("Waiting for Hammer of Wrath Cooldown")
+ return true
+ end
+ -- * Divine Storm
+ -- divine_storm,if=buff.divine_crusader.react&buff.avenging_wrath.up
+ if cast.able.divineStorm() then
+ if buff.divineCrusader.exists() and buff.avengingWrath.exists() then
+ if cast.divineStorm() then
+ ui.debug("Casting Divine Storm - Avenging Wrath")
+ return true
+ end
+ end
+ end
+ -- * Templar's Verdict
+ -- templars_verdict,if=buff.avenging_wrath.up
+ if (ui.useST(8,2) or not spell.divineStorm.known()) and cast.able.templarsVerdict() then
+ if buff.avengingWrath.exists() then
+ if cast.templarsVerdict() then
+ ui.debug("Casting Templar's Verdict - Avenging Wrath")
+ return true
+ end
+ end
+ end
+ -- * Hammer of the Righteous
+ -- hammer_of_the_righteous,if=active_enemies>=4
+ if ui.useAOE(8,4) and cast.able.hammerOfTheRighteous() then
+ if cast.hammerOfTheRighteous() then
+ ui.debug("Casting Hammer of the Righteous")
+ return true
+ end
+ end
+ -- * Crusader Strike
+ -- crusader_strike
+ if (ui.useST(8,4) or not spell.hammerOfTheRighteous.known()) and cast.able.crusaderStrike() then
+ if cast.crusaderStrike() then
+ ui.debug("Casting Crusader Strike")
+ return true
+ end
+ end
+ -- * Wait
+ -- wait,sec=cooldown.crusader_strike.remains,if=cooldown.crusader_strike.remains>0&cooldown.crusader_strike.remains<=0.2
+ if cd.crusaderStrike.remain() > 0 and cd.crusaderStrike.remain() <= 0.2 then
+ -- ui.debug("Waiting for Crusader Strike Cooldown")
+ return true
+ end
+ -- * Exorcism
+ -- exorcism,if=active_enemies>=2&active_enemies<=4&set_bonus.tier15_2pc_melee&glyph.mass_exorcism.enabled
+ -- if ui.useAOE(8,2) and ui.useAOE(8,4) and talent.glyphOfMassExorcism and cast.able.exorcism() then
+ -- if cast.exorcism() then
+ -- ui.debug("Casting Exorcism - AOE")
+ -- return true
+ -- end
+ -- end
+ -- * Judgment
+ -- judgment
+ if cast.able.judgment(units.dyn30) then
+ if cast.judgment(units.dyn30) then
+ ui.debug("Casting Judgment")
+ return true
+ end
+ end
+ -- * Wait
+ -- wait,sec=cooldown.judgment.remains,if=cooldown.judgment.remains>0&cooldown.judgment.remains<=0.2
+ if cd.judgment.remain() > 0 and cd.judgment.remain() <= 0.2 then
+ -- ui.debug("Waiting for Judgment Cooldown")
+ return true
+ end
+ -- * Divine Storm
+ -- divine_storm,if=buff.divine_crusader.react
+ if cast.able.divineStorm() then
+ if buff.divineCrusader.exists() then
+ if cast.divineStorm() then
+ ui.debug("Casting Divine Storm - Divine Crusader")
+ return true
+ end
+ end
+ end
+ -- * Templar's Verdict
+ -- templars_verdict,if=buff.divine_purpose.react
+ if (ui.useST(8,2) or not spell.divineStorm.known()) and cast.able.templarsVerdict() then
+ if buff.divinePurpose.exists() then
+ if cast.templarsVerdict() then
+ ui.debug("Casting Templar's Verdict - Divine Purpose")
+ return true
+ end
+ end
+ end
+ -- * Exorcism
+ -- exorcism
+ if ui.checked("Exorcism") and cast.able.exorcism() then
+ if cast.exorcism() then
+ ui.debug("Casting Exorcism")
+ return true
+ end
+ end
+ -- * Wait
+ -- wait,sec=cooldown.exorcism.remains,if=cooldown.exorcism.remains>0&cooldown.exorcism.remains<=0.2
+ if cd.exorcism.remain() > 0 and cd.exorcism.remain() <= 0.2 then
+ -- ui.debug("Waiting for Exorcism Cooldown")
+ return true
+ end
+ -- * Templar's Verdict
+ -- templars_verdict,if=buff.tier15_4pc_melee.up&active_enemies<4
+ -- if (ui.useST(8,2) or not spell.divineStorm.known()) and cast.able.templarsVerdict() then
+ -- if buff.tier15_4pc_melee.exists() and #enemies.yards8 < 4 then
+ -- if cast.templarsVerdict() then
+ -- ui.debug("Casting Templar's Verdict - T15 4PC")
+ -- return true
+ -- end
+ -- end
+ -- end
+ -- * Divine Storm
+ -- divine_storm,if=active_enemies>=2&buff.inquisition.remains>4
+ if ui.useAOE(8,2) and cast.able.divineStorm() then
+ if buff.inquisition.remain() > 4 then
+ if cast.divineStorm() then
+ ui.debug("Casting Divine Storm - Inquisition")
+ return true
+ end
+ end
+ end
+ -- * Templar's Verdict
+ -- templars_verdict,if=buff.inquisition.remains>4
+ if (ui.useST(8,2) or not spell.divineStorm.known()) and cast.able.templarsVerdict() then
+ if buff.inquisition.remain() > 4 then
+ if cast.templarsVerdict() then
+ ui.debug("Casting Templar's Verdict - Inquisition")
+ return true
+ end
+ end
+ end
+ -- * Holy Prism
+ -- holy_prism,if=talent.holy_prism.enabled
+ if ui.checked("Holy Prism") and talent.holyPrism and cast.able.holyPrism() then
+ if cast.holyPrism() then
+ ui.debug("Casting Holy Prism")
+ return true
+ end
+ end
+ end -- End In Combat Rotation
+ end
+end -- End Action List - Combat
+
+----------------
+--- ROTATION ---
+----------------
+local function runRotation()
+ ---------------------
+ --- Define Locals ---
+ ---------------------
+ -- BR API Locals
+ buff = br.player.buff
+ cast = br.player.cast
+ cd = br.player.cd
+ debuff = br.player.debuff
+ enemies = br.player.enemies
+ holyPower = br.player.power.holyPower
+ module = br.player.module
+ spell = br.player.spell
+ spells = br.player.spells
+ talent = br.player.talent
+ ui = br.player.ui
+ unit = br.player.unit
+ units = br.player.units
+ -- Units
+ units.get(5)
+ units.get(30)
+ -- Enemies
+ enemies.get(5)
+ enemies.get(8)
+ enemies.get(10)
+ -- General Locals
+ var.haltProfile = (unit.inCombat() and var.profileStop) or unit.mounted() or unit.flying() or br.functions.misc:pause() or ui.mode.rotation == 4
+
+
+ ---------------------
+ --- Begin Profile ---
+ ---------------------
+ -- Profile Stop | Pause
+ if not unit.inCombat() and not unit.exists("target") and var.profileStop then
+ var.profileStop = false
+ elseif var.haltProfile then
+ return true
+ else
+ ---------------------------------
+ --- Out Of Combat - Rotations ---
+ ---------------------------------
+ -------------
+ --- Extra ---
+ -------------
+ if actionList.Extras() then return true end
+ -----------------
+ --- Defensive ---
+ -----------------
+ if actionList.Defensive() then return true end
+ ------------------
+ --- Pre-Combat ---
+ ------------------
+ if actionList.PreCombat() then return true end
+ if unit.inCombat() then
+ -----------------
+ --- Cooldowns ---
+ -----------------
+ if actionList.Cooldowns() then return true end
+ -----------------
+ --- Interrupt ---
+ -----------------
+ if actionList.Interrupt() then return true end
+ --------------
+ --- Combat ---
+ --------------
+ if actionList.Combat() then return true end
+ end
+ end -- Pause
+end -- End runRotation
+local id = 70
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
+ name = rotationName,
+ toggles = createToggles,
+ options = createOptions,
+ run = runRotation,
+})
diff --git a/Rotations/Paladin/Retribution/RetributionCuteOne.lua b/Rotations/Paladin/Retribution/RetributionCuteOne.lua
index 774d9c65c3..23726549ba 100644
--- a/Rotations/Paladin/Retribution/RetributionCuteOne.lua
+++ b/Rotations/Paladin/Retribution/RetributionCuteOne.lua
@@ -166,17 +166,15 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
--Cooldown Key Toggle
- br.ui:createDropdownWithout(section, "Cooldown Mode", br.dropOptions.Toggle, 3)
+ br.ui:createDropdownWithout(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 3)
--Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupts Key Toggle
- br.ui:createDropdownWithout(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
-- Wake of Ashes Key Toggle
- br.ui:createDropdownWithout(section, "Wake Mode", br.dropOptions.Toggle, 6)
- -- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Wake Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = {
@@ -216,14 +214,14 @@ local canGlory = function()
local optionValue = ui.value("Word of Glory")
local otherCounter = 0
if holyPower() >= 3 then
- if #br.friend == 1 then
+ if #br.engines.healingEngine.friend == 1 then
if unit.hp("player") <= optionValue then
var.gloryUnit = "player"
return true
end
end
- for i = 1, #br.friend do
- local thisUnit = br.friend[i].unit
+ for i = 1, #br.engines.healingEngine.friend do
+ local thisUnit = br.engines.healingEngine.friend[i].unit
local thisHP = unit.hp(thisUnit)
if thisHP < optionValue then
-- Emergency Single
@@ -233,8 +231,8 @@ local canGlory = function()
end
-- Group Heal
if otherCounter < 2 then
- for j = 1, #br.friend do
- local otherUnit = br.friend[j].unit
+ for j = 1, #br.engines.healingEngine.friend do
+ local otherUnit = br.engines.healingEngine.friend[j].unit
local otherHP = unit.hp(otherUnit)
local distanceFromYou = unit.distance(otherUnit, "player")
if distanceFromYou < 30 and otherHP < optionValue then
@@ -269,9 +267,9 @@ local getHealUnitOption = function(option, checkForbearance)
if thisTar == 5 then
thisUnit = var.lowestUnit
-- Get the next lowest unit if lowest unit has Forbearance debuff
- if checkForbearance and #br.friend > 1 and debuff.forbearance.exists(thisUnit) then
- for i = 1, #br.friend do
- local nextUnit = br.friend[i].unit
+ if checkForbearance and #br.engines.healingEngine.friend > 1 and debuff.forbearance.exists(thisUnit) then
+ for i = 1, #br.engines.healingEngine.friend do
+ local nextUnit = br.engines.healingEngine.friend[i].unit
if not debuff.forbearance.exists(nextUnit) then
thisUnit = nextUnit
break
@@ -322,8 +320,6 @@ end -- End Action List - Extras
-- Action List - Defensives
actionList.Defensive = function()
if ui.useDefensive() then
- -- Basic Healing Module
- module.BasicHealing()
-- Blessing of Protection
if ui.checked("Blessing of Protection", true) then
var.protectionUnit = getHealUnitOption("Blessing of Protection Target")
@@ -1044,7 +1040,7 @@ local runRotation = function()
-- variable,name=ds_castable,value=spell_targets.divine_storm=2&!(runeforge.final_verdict&talent.righteous_verdict.enabled&conduit.templars_vindication.enabled)|spell_targets.divine_storm>2|buff.empyrean_power.up&debuff.judgment.down&buff.divine_purpose.down
-- var.dsUnits = ((ui.mode.rotation == 1 and ((#enemies.yards8 == 2 and not (runeforge.finalVerdict.equiped and talent.righteousVerdict and conduit.templarsVindication.enabled)) or #enemies.yards8 > 2)) or (ui.mode.rotation == 2 and #enemies.yards8 > 0))
-- var.dsCastable = (var.dsUnits or (buff.empyreanPower.exists() and not debuff.judgment.exists(units.dyn8) and not buff.divinePurpose.exists()))
- var.lowestUnit = br.friend[1].unit
+ var.lowestUnit = br.engines.healingEngine.friend[1].unit
var.resable = unit.player("target") and unit.deadOrGhost("target") and unit.friend("target", "player")
var.timeToHPG = 99
if unit.level() >= 46 then
@@ -1186,13 +1182,14 @@ local runRotation = function()
-- br.debug.cpu.rotation.inCombat = debugprofilestop() - startTime
-- end -- End In Combat
end -- End Profile
-end -- runRotation
+end -- runRotation
local id = 70
-if br.rotations[id] == nil then
- br.rotations[id] = {}
+local expansion = br.isRetail
+if br.loader.rotations[id] == nil then
+ br.loader.rotations[id] = {}
end
br._G.tinsert(
- br.rotations[id],
+ br.loader.rotations[id],
{
name = rotationName,
toggles = createToggles,
diff --git a/Rotations/Priest/Initial/InitialPriest.lua b/Rotations/Priest/Initial/InitialPriest.lua
index 2ada019934..05f07b93e6 100644
--- a/Rotations/Priest/Initial/InitialPriest.lua
+++ b/Rotations/Priest/Initial/InitialPriest.lua
@@ -48,11 +48,11 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
--Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -158,14 +158,14 @@ actionList.PreCombat = function()
-- Smite
if cast.able.smite("target") and not unit.moving() then
if cast.smite("target") then
- br.addonDebug("Casting Smite [Precombat]")
+ br.functions.misc:addonDebug("Casting Smite [Precombat]")
return true
end
end
-- Shadow Word: Pain
if cast.able.shadowWordPain("target") and debuff.shadowWordPain.refresh("target") then
if cast.shadowWordPain("target") then
- br.addonDebug("Casting Shadow Word: Pain [Precombat]")
+ br.functions.misc:addonDebug("Casting Shadow Word: Pain [Precombat]")
return true
end
end
@@ -197,7 +197,7 @@ actionList.Combat = function()
-- Shadow Word: Pain
if cast.able.shadowWordPain(units.dyn40) and debuff.shadowWordPain.refresh(units.dyn40) then
if cast.shadowWordPain(units.dyn40) then
- br.addonDebug("Casting Shadow Word: Pain")
+ br.functions.misc:addonDebug("Casting Shadow Word: Pain")
return true
end
end
@@ -211,7 +211,7 @@ actionList.Combat = function()
-- Smite
if cast.able.smite() and not unit.moving() then
if cast.smite() then
- br.addonDebug("Casting Smite")
+ br.functions.misc:addonDebug("Casting Smite")
return true
end
end
@@ -237,7 +237,7 @@ local function runRotation()
units = br.player.units
-- General Locals
profileStop = profileStop or false
- haltProfile = (unit.inCombat() and profileStop) or IsMounted() or br.pause() or mode.rotation == 2
+ haltProfile = (unit.inCombat() and profileStop) or IsMounted() or br.functions.misc:pause() or mode.rotation == 2
var.friendUnit = (unit.player("target") and unit.friend("target")) and "target" or "player"
-- Units
units.get(5) -- Makes a variable called, units.dyn5
@@ -281,8 +281,9 @@ local function runRotation()
end -- Pause
end -- End runRotation
local id = 1452 -- Change to the spec id profile is for.
-if br.rotations[id] == nil then br.rotations[id] = {} end
-tinsert(br.rotations[id], {
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Rogue/Initial/InitialRogue.lua b/Rotations/Rogue/Initial/InitialRogue.lua
index fd0cc16a3e..4c24341bfc 100644
--- a/Rotations/Rogue/Initial/InitialRogue.lua
+++ b/Rotations/Rogue/Initial/InitialRogue.lua
@@ -12,8 +12,8 @@ local function createToggles()
br.ui:createToggle(RotationModes, "Rotation", 1, 0)
-- Defensive Button
local DefensiveModes = {
- [1] = { mode = "On", value = 1, overlay = "Defensive Enabled", tip = "Includes Defensive Cooldowns.", highlight = 1, icon = br.player.spells.crimsonVial },
- [2] = { mode = "Off", value = 2, overlay = "Defensive Disabled", tip = "No Defensives will be used.", highlight = 0, icon = br.player.spells.crimsonVial }
+ [1] = { mode = "On", value = 1, overlay = "Defensive Enabled", tip = "Includes Defensive Cooldowns.", highlight = 1, icon = br.player.spells.evasion },
+ [2] = { mode = "Off", value = 2, overlay = "Defensive Disabled", tip = "No Defensives will be used.", highlight = 0, icon = br.player.spells.evasion }
};
br.ui:createToggle(DefensiveModes, "Defensive", 2, 0)
-- Interrupts Button
@@ -40,28 +40,24 @@ local function createOptions()
br.ui:createDropdownWithout(section, "Eviscerate Execute",
{ "|cffFFFF00Enabled Notify", "|cff00FF00Enabled", "|cffFF0000Disabled" }, 2,
"Options for using Eviscerate when the damage from it will kill the unit.")
- -- Sprint
- br.ui:createCheckbox(section, "Sprint", "|cffFFFFFFWill use sprint automatically.")
-- Stealth
br.ui:createCheckbox(section, "Stealth", "|cffFFFFFFWill use stealth at all times.")
-- Stealth Breaker
br.ui:createDropdownWithout(section, "Stealth Breaker",
- { "|cff00FF00Ambush", "|cffFFFF00Cheapshot", "|cffFF0000Sinister Strike" }, 3,
+ { "|cff00FF00Ambush", "|cffFF0000Sinister Strike" }, 3,
"|cffFFFFFFSet what to break Stealth with.")
br.ui:checkSectionState(section)
-------------------------
--- DEFENSIVE OPTIONS ---
-------------------------
section = br.ui:createSection(br.ui.window.profile, "Defensive")
- -- Crimson Vial
- br.ui:createSpinner(section, "Crimson Vial", 70, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ -- Evasion
+ br.ui:createSpinner(section, "Evasion", 70, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
br.ui:checkSectionState(section)
-------------------------
--- INTERRUPT OPTIONS ---
-------------------------
section = br.ui:createSection(br.ui.window.profile, "Interrupt")
- -- Kick
- br.ui:createCheckbox(section, "Kick", "|cffFFFFFFUse Kick")
-- Interrupt Percentage
br.ui:createSpinnerWithout(section, "Interrupt At", 0, 0, 95, 5,
"|cffFFFFFFCast Percent to Cast At (0 is random)")
@@ -71,13 +67,11 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 6)
-- Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupts Key Toggle
- br.ui:createDropdownWithout(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
- -- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -104,7 +98,6 @@ local units
-- General Locals
local haltProfile
local profileStop
-local eviscerateDamage
local finishHim
-- Profile Specific Locals
local actionList = {}
@@ -117,20 +110,19 @@ local function eviscerateFinish(thisUnit)
local desc = br._G.C_Spell.GetSpellDescription(spell.eviscerate)
local damage = 0
finishHim = false
- if ui.value("Eviscerate Execute") ~= 3 and combo() > 0 and not unit.isDummy(thisUnit) then
- local comboStart = desc:find(" " .. combo() .. " ", 1, true)
- if comboStart ~= nil then
- comboStart = comboStart + 2
- local damageList = desc:sub(comboStart, desc:len())
- comboStart = damageList:find(": ", 1, true) + 2
- damageList = damageList:sub(comboStart, desc:len())
- local comboEnd = damageList:find(" ", 1, true) - 1
- damageList = damageList:sub(1, comboEnd)
- damage = damageList:gsub(",", "")
- end
- eviscerateDamage = tonumber(damage)
- finishHim = tonumber(damage) >= unit.health(thisUnit) and unit.health(thisUnit) > 0
+ if ui.value("Eviscerate Execute") == 3 or combo() == 0 or unit.isDummy(thisUnit) then return false end
+ local comboStart = desc:find(" " .. combo() .. " ", 1, true)
+ if comboStart ~= nil then
+ comboStart = comboStart + 2
+ local damageList = desc:sub(comboStart, desc:len())
+ comboStart = damageList:find(": ", 1, true) + 2
+ damageList = damageList:sub(comboStart, desc:len())
+ local comboEnd = damageList:find(" ", 1, true) - 1
+ damageList = damageList:sub(1, comboEnd)
+ damage = damageList:gsub(",", "")
end
+ local lower = tonumber(string.match(damage, "^(%d+)%-%d+$")) or 0
+ finishHim = tonumber(lower) >= unit.health(thisUnit)
return finishHim
end
@@ -139,17 +131,10 @@ end
--------------------
-- Action List - Extras
actionList.Extras = function()
- -- Crippling Poison
- if cast.able.cripplingPoison() and not buff.cripplingPoison.exists() then
- if cast.cripplingPoison() then
- ui.debug("Casting Crippling Poison")
- return true
- end
- end
- -- Instant Poison
- if cast.able.instantPoison() and not buff.instantPoison.exists() then
- if cast.instantPoison() then
- ui.debug("Casting Instant Poison")
+ -- Deadly Poison
+ if cast.able.deadlyPoison() and not buff.deadlyPoison.exists() then
+ if cast.deadlyPoison() then
+ ui.debug("Casting Deadly Poison")
return true
end
end
@@ -162,21 +147,14 @@ actionList.Extras = function()
return true
end
end
- -- Sprint
- if ui.checked("Sprint") and cast.able.sprint() then
- if cast.sprint() then
- ui.debug("Casting Sprint")
- return true
- end
- end
end
-- Action List - Defensive
actionList.Defensive = function()
- --Crimson Vial
- if unit.level() >= 8 and ui.checked("Crimson Vial") then
- if cast.able.crimsonVial() and unit.inCombat() and unit.hp() <= ui.value("Crimson Vial") then
- if cast.crimsonVial() then
- ui.debug("Casting Crimson Vial")
+ --Evasion
+ if unit.level() >= 8 and ui.checked("Evasion") then
+ if cast.able.evasion() and unit.inCombat() and unit.hp() <= ui.value("Evasion") then
+ if cast.evasion() then
+ ui.debug("Casting Evasion")
return true
end
end
@@ -186,18 +164,7 @@ end -- End Action List - Defensive
-- Action List - Interrupt
actionList.Interrupt = function()
if ui.useInterrupt() then
- -- Kick
- if ui.checked("Kick") then
- for i = 1, #enemies.yards5f do
- local thisUnit = enemies.yards5f[i]
- if cast.able.kick(thisUnit) and unit.interruptable(thisUnit, ui.value("Interrupt At")) then
- if cast.kick(thisUnit) then
- ui.debug("Casting Kick on " .. unit.name(thisUnit))
- return true
- end
- end
- end
- end
+
end
end -- End Action List - Interrupt
@@ -206,22 +173,16 @@ actionList.PreCombat = function()
if unit.valid("target") and unit.distance("target") < 5 then
if buff.stealth.exists() or unit.level() < 3 then
--Ambush
- if ui.value("Stealth Breaker") == 1 and cast.able.ambush("target") then
+ if ui.value("Stealth Breaker") == 1 and cast.able.ambush("target") and not unit.facing("target", "player") then
if cast.ambush("target") then
ui.debug("Casting Ambush [Pre-Combat]")
return true
end
end
- -- Cheap Shot
- if (ui.value("Stealth Breaker") == 2 or (ui.value("Stealth Breaker") == 1 and unit.level() < 7)) and cast.able.cheapShot("target") then
- if cast.cheapShot("target") then
- ui.debug("Casting Cheap Shot [Pre-Combat]")
- return true
- end
- end
-- Sinister Strike
- if ui.value("Stealth Breaker") == 3 and cast.able.sinisterStrike("target")
- and not unit.inCombat() and (combo() < 5 or unit.level() < 3)
+ if (ui.value("Stealth Breaker") == 2 or (ui.value("Stealth Breaker") == 1
+ and (unit.level() < 6 or unit.facing("target", "player"))))
+ and cast.able.sinisterStrike("target") and not unit.inCombat() and (combo() < 5 or unit.level() < 3)
then
if cast.sinisterStrike("target") then
ui.debug("Casting Sinister Strike [Pre-Combat]")
@@ -230,7 +191,7 @@ actionList.PreCombat = function()
end
end
-- Start Attack
- if cast.able.autoAttack("target") and not unit.inCombat() and energy() < 45 then
+ if cast.able.autoAttack("target") and not unit.inCombat() and energy() < 40 then
if cast.autoAttack("target") then
ui.debug("Casting Auto Attack [Pre-Combat]")
return true
@@ -262,20 +223,6 @@ actionList.Combat = function()
end
end
end
- --Ambush
- if cast.able.ambush() and buff.stealth.exists() then
- if cast.ambush() then
- ui.debug("Casting Ambush")
- return true
- end
- end
- --Slice and Dice
- if cast.able.sliceAndDice() and unit.level() >= 9 and combo() > 4 and not buff.sliceAndDice.exists() then
- if cast.sliceAndDice() then
- ui.debug("Casting Slice and Dice")
- return true
- end
- end
--Eviscerate
if cast.able.eviscerate() and (combo() > 4 or unit.hp(units.dyn5) < 20) then
if cast.eviscerate() then
@@ -313,7 +260,7 @@ local function runRotation()
units = br.player.units
-- General Locals
profileStop = profileStop or false
- haltProfile = (unit.inCombat() and profileStop) or unit.mounted() or br.pause() or mode.rotation == 2
+ haltProfile = (unit.inCombat() and profileStop) or unit.mounted() or br.functions.misc:pause() or mode.rotation == 2
-- Units
units.get(5) -- Makes a variable called, units.dyn5
units.get(40) -- Makes a variable called, units.dyn40
@@ -321,7 +268,6 @@ local function runRotation()
-- Enemies
enemies.get(5, "player", false, true) -- makes enemies.yards5f
- if eviscerateDamage == nil then eviscerateDamage = 0 end
finishHim = eviscerateFinish(units.dyn5)
-- Pause Timer
@@ -349,7 +295,7 @@ local function runRotation()
--- Pre-Combat ---
------------------
if actionList.PreCombat() then return true end
- if unit.inCombat() and not profileStop and unit.exists(units.dyn5) then
+ if unit.inCombat() and not profileStop and unit.exists(units.dyn5) and not buff.stealth.exists() then
------------------------------
--- In Combat - Interrupts ---
------------------------------
@@ -362,8 +308,9 @@ local function runRotation()
end -- Pause
end -- End runRotation
local id = 1453 -- Change to the spec id profile is for.
-if br.rotations[id] == nil then br.rotations[id] = {} end
-br._G.tinsert(br.rotations[id], {
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Rogue/Subtlety/SubtletyCuteOne.lua b/Rotations/Rogue/Subtlety/SubtletyCuteOne.lua
index 3ca86f1852..32cf374585 100644
--- a/Rotations/Rogue/Subtlety/SubtletyCuteOne.lua
+++ b/Rotations/Rogue/Subtlety/SubtletyCuteOne.lua
@@ -1,10 +1,10 @@
-------------------------------------------------------
-- Author = CuteOne
--- Patch = 10.2
+-- Patch = 5.4.8 (Mists of Pandaria)
-- Patch should be the latest patch you've updated the rotation for (i.e., 9.2.5)
-- Coverage = 100%
-- Coverage should be your estimated percent coverage for class mechanics (i.e., 100%)
--- Status = Limited
+-- Status = Full
-- Status should be one of: Full, Limited, Sporadic, Inactive, Unknown
-- Readiness = Basic
-- Readiness should be one of: Raid, NoRaid, Basic, Development, Untested
@@ -13,128 +13,101 @@
-------------------------------------------------------
local rotationName = "CuteOne"
+
---------------
--- Toggles ---
---------------
-local createToggles = function()
+local function createToggles()
-- Rotation Button
local RotationModes = {
- [1] = { mode = "Auto", value = 1, overlay = "Automatic Rotation", tip = "Swaps between Single and Multiple based on number of targets in range.", highlight = 1, icon = br.player.spells.shurikenStorm },
- [2] = { mode = "Mult", value = 2, overlay = "Multiple Target Rotation", tip = "Multiple target rotation used.", highlight = 0, icon = br.player.spells.shurikenStorm },
+ [1] = { mode = "Auto", value = 1, overlay = "Automatic Rotation", tip = "Swaps between Single and Multiple based on number of targets in range.", highlight = 1, icon = br.player.spells.fanOfKnives },
+ [2] = { mode = "Mult", value = 2, overlay = "Multiple Target Rotation", tip = "Multiple target rotation used.", highlight = 0, icon = br.player.spells.fanOfKnives },
[3] = { mode = "Sing", value = 3, overlay = "Single Target Rotation", tip = "Single target rotation used.", highlight = 0, icon = br.player.spells.backstab },
- [4] = { mode = "Off", value = 4, overlay = "DPS Rotation Disabled", tip = "Disable DPS Rotation", highlight = 0, icon = br.player.spells.crimsonVial }
+ [4] = { mode = "Off", value = 4, overlay = "DPS Rotation Disabled", tip = "Disable DPS Rotation", highlight = 0, icon = br.player.spells.stealth }
}
br.ui:createToggle(RotationModes, "Rotation", 1, 0)
-- Cooldown Button
local CooldownModes = {
[1] = { mode = "Auto", value = 1, overlay = "Cooldowns Automated", tip = "Automatic Cooldowns - Boss Detection.", highlight = 1, icon = br.player.spells.shadowBlades },
- [2] = { mode = "On", value = 1, overlay = "Cooldowns Enabled", tip = "Cooldowns used regardless of target.", highlight = 0, icon = br.player.spells.shadowBlades },
+ [2] = { mode = "On", value = 2, overlay = "Cooldowns Enabled", tip = "Cooldowns used regardless of target.", highlight = 0, icon = br.player.spells.shadowBlades },
[3] = { mode = "Off", value = 3, overlay = "Cooldowns Disabled", tip = "No Cooldowns will be used.", highlight = 0, icon = br.player.spells.shadowBlades }
}
br.ui:createToggle(CooldownModes, "Cooldown", 2, 0)
-- Defensive Button
- DefensiveModes = {
+ local DefensiveModes = {
[1] = { mode = "On", value = 1, overlay = "Defensive Enabled", tip = "Includes Defensive Cooldowns.", highlight = 1, icon = br.player.spells.evasion },
[2] = { mode = "Off", value = 2, overlay = "Defensive Disabled", tip = "No Defensives will be used.", highlight = 0, icon = br.player.spells.evasion }
}
br.ui:createToggle(DefensiveModes, "Defensive", 3, 0)
-- Interrupt Button
- InterruptModes = {
+ local InterruptModes = {
[1] = { mode = "On", value = 1, overlay = "Interrupts Enabled", tip = "Includes Basic Interrupts.", highlight = 1, icon = br.player.spells.kick },
[2] = { mode = "Off", value = 2, overlay = "Interrupts Disabled", tip = "No Interrupts will be used.", highlight = 0, icon = br.player.spells.kick }
}
br.ui:createToggle(InterruptModes, "Interrupt", 4, 0)
- -- Shadow Dance Button
- ShadowDanceModes = {
- [1] = { mode = "On", value = 1, overlay = "Shadow Dance Enabled", tip = "Rotation will use Shadow Dance.", highlight = 1, icon = br.player.spells.shadowDance },
- [2] = { mode = "Off", value = 2, overlay = "Shadow Dance Disabled", tip = "Rotation will not use Shadow Dance. Useful for pooling SD charges as you near dungeon bosses.", highlight = 0, icon = br.player.spells.shadowDance },
- }
- br.ui:createToggle(ShadowDanceModes, "ShadowDance", 5, 0)
-- Pick Pocket Button
- PickPocketModes = {
- [1] = { mode = "Auto", value = 1, overlay = "Auto Pick Pocket Enabled", tip = "Profile will attempt to Pick Pocket prior to combat.", highlight = 1, icon = br.player.spells.pickPocket },
- [2] = { mode = "Only", value = 2, overlay = "Only Pick Pocket Enabled", tip = "Profile will attempt to Sap and only Pick Pocket, no combat.", highlight = 0, icon = br.player.spells.pickPocket },
- [3] = { mode = "Off", value = 3, overlay = "Pick Pocket Disabled", tip = "Profile will not use Pick Pocket.", highlight = 0, icon = br.player.spells.pickPocket }
+ local PickPocketModes = {
+ [1] = { mode = "Auto", value = 1, overlay = "Pick Pocket Auto", tip = "Automatically Pick Pocket before engaging, minimal delay.", highlight = 1, icon = br.player.spells.pickPocket },
+ [2] = { mode = "Only", value = 2, overlay = "Pick Pocket Only", tip = "Only Pick Pocket targets (Sap > Pick Pocket > Clear Target).", highlight = 0, icon = br.player.spells.pickPocket },
+ [3] = { mode = "Off", value = 3, overlay = "Pick Pocket Disabled", tip = "Never Pick Pocket.", highlight = 0, icon = br.player.spells.pickPocket }
}
- br.ui:createToggle(PickPocketModes, "PickPocket", 6, 0)
+ br.ui:createToggle(PickPocketModes, "PickPocket", 5, 0)
end
---------------
--- OPTIONS ---
---------------
-local createOptions = function()
+local function createOptions()
local optionTable
- local rotationOptions = function()
+ local function rotationOptions()
local section
- local alwaysCdNever = { "Always", "|cff0000ffCD", "|cffff0000Never" }
- local alwaysCdAoENever = { "Always", "|cff008000AOE", "|cffffff00AOE/CD", "|cff0000ffCD", "|cffff0000Never" }
-----------------------
--- GENERAL OPTIONS ---
-----------------------
section = br.ui:createSection(br.ui.window.profile, "General")
- -- Poison
- br.ui:createDropdownWithout(section, "Lethal Poison", { "Instant", "Wound", "None" }, 1, "Lethal Poison to Apply")
- br.ui:createDropdownWithout(section, "Non-Lethal Poison", { "Crippling", "Numbing", "None" }, 1,
- "Non-Lethal Poison to Apply")
- -- Priority Rotation
- br.ui:createCheckbox(section, "Priority Rotation")
- -- Stealth
- br.ui:createDropdown(section, "Stealth", { "|cff00FF00Always", "|cffFFDD00PrePot", "|cffFF000020Yards" }, 1,
- "Stealthing method.")
+ -- Dummy DPS Test
+ br.ui:createSpinner(section, "DPS Testing", 5, 5, 60, 5,
+ "|cffFFFFFFSet to desired time for test in minutes. Min: 5 / Max: 60 / Interval: 5")
+ -- Auto Stealth
+ br.ui:createCheckbox(section, "Auto Stealth")
-- Stealth Breaker
br.ui:createDropdownWithout(section, "Stealth Breaker",
- { "|cff00FF00Shadowstrike", "|cffFFFF00Cheapshot", "|cffFF0000Sinister Strike" }, 3,
+ { "|cff00FF00Ambush", "|cffFF0000Hemorrhage" }, 1,
"|cffFFFFFFSet what to break Stealth with.")
- -- Shadowstep
- br.ui:createCheckbox(section, "Shadowstep")
- -- Shadowstrike
- br.ui:createSpinnerWithout(section, "SS Range", 5, 5, 25, 5, "|cffFFBB00Shadow Strike range, 5 = Melee")
- -- Shuriken Toss OOR
- br.ui:createSpinner(section, "Shuriken Toss OOR", 85, 5, 100, 5,
- "|cffFFBB00Check to use Shuriken Toss out of range and energy to use at.")
- -- Tricks of the Trade
- br.ui:createCheckbox(section, "Tricks of the Trade on Focus")
- -- Pre-Pull
- br.ui:createCheckbox(section, "Pre-Pull", "|cffFFFFFFSet to use Pre-Pull (DBM Required).")
- -- Dummy DPS Test
- br.ui:createSpinner(section, "DPS Testing", 5, 5, 60, 5,
- "|cffFFFFFFSet to desired time for test in minuts. Min: 5 / Max: 60 / Interval: 5")
+ -- Poisons
+ br.ui:createDropdownWithout(section, "Lethal Poison", { "|cffFFFFFFInstant", "|cffFFFFFFDeadly", "|cffFFFFFFWound" }, 2,
+ "|cffFFFFFFSelect Lethal Poison to use.")
+ br.ui:createDropdownWithout(section, "Non-Lethal Poison", { "|cffFFFFFFNone", "|cffFFFFFFCrippling", "|cffFFFFFFMind-Numbing", "|cffFFFFFFParalytic" }, 2,
+ "|cffFFFFFFSelect Non-Lethal Poison to use.")
br.ui:checkSectionState(section)
------------------------
--- COOLDOWN OPTIONS ---
------------------------
section = br.ui:createSection(br.ui.window.profile, "Cooldowns")
- -- Cold Blood
- br.ui:createDropdownWithout(section, "Cold Blood", alwaysCdNever, 2, "|cffFFFFFFWhen to use Cold Blood.")
- -- Echoing Reprimand
- br.ui:createDropdownWithout(section, "Echoing Reprimand", alwaysCdNever, 2,
- "|cffFFFFFFWhen to use Echoing Reprimand")
- -- Flagellation
- br.ui:createDropdownWithout(section, "Flagellation", alwaysCdAoENever, 3, "|cffFFFFFFWhen to use Flagellation")
- -- Racial
- br.ui:createCheckbox(section, "Racial")
- -- Secret Technique
- br.ui:createDropdownWithout(section, "Secret Technique", alwaysCdAoENever, 3,
- "|cffFFFFFFWhen to use Secret Technique.")
- -- Sepsis
- br.ui:createDropdownWithout(section, "Sepsis", alwaysCdNever, 2, "|cffFFFFFFWhen to use Sepsis.")
+ -- Shadowstep
+ br.ui:createCheckbox(section, "Shadowstep")
-- Shadow Blades
- br.ui:createDropdownWithout(section, "Shadow Blades", alwaysCdAoENever, 3, "|cffFFFFFFWhen to use Shadow Blades.")
+ br.ui:createDropdownWithout(section, "Shadow Blades", br.ui.dropOptions.AlwaysCdAoeNever,
+ 2, "|cffFFFFFFWhen to use Shadow Blades.")
-- Shadow Dance
- br.ui:createDropdownWithout(section, "Shadow Dance", alwaysCdAoENever, 3, "|cffFFFFFFWhen to use Shadow Dance.")
- -- Shuriken Tornado
- br.ui:createDropdownWithout(section, "Shuriken Tornado", alwaysCdAoENever, 3,
- "|cffFFFFFFWhen to use Shuriken Tornado.")
- -- Symbols of Death
- br.ui:createDropdownWithout(section, "Symbols of Death", alwaysCdNever, 2,
- "|cffFFFFFFWhen to use Symbols of Death")
- -- Thistle Tea
- br.ui:createDropdownWithout(section, "Thistle Tea", alwaysCdNever, 2, "|cffFFFFFFWhen to use Thistle Tea")
+ br.ui:createDropdownWithout(section, "Shadow Dance", br.ui.dropOptions.AlwaysCdAoeNever,
+ 2, "|cffFFFFFFWhen to use Shadow Dance.")
+ -- Vanish
+ br.ui:createDropdownWithout(section, "Vanish", br.ui.dropOptions.AlwaysCdAoeNever,
+ 2, "|cffFFFFFFWhen to use Vanish.")
+ -- Preparation
+ br.ui:createCheckbox(section, "Preparation")
+ -- Premeditation
+ br.ui:createCheckbox(section, "Premeditation")
+ -- Marked for Death
+ br.ui:createCheckbox(section, "Marked for Death")
+ -- Use Racial
+ br.ui:createCheckbox(section, "Use Racial")
+ -- Use Combat Potion
+ br.ui:createCheckbox(section, "Use Combat Potion")
-- Trinkets
br.player.module.BasicTrinkets(section)
- -- Vanish
- br.ui:createCheckbox(section, "Vanish")
br.ui:checkSectionState(section)
-------------------------
--- DEFENSIVE OPTIONS ---
@@ -142,14 +115,14 @@ local createOptions = function()
section = br.ui:createSection(br.ui.window.profile, "Defensive")
-- Cloak of Shadows
br.ui:createCheckbox(section, "Cloak of Shadows")
- -- Crimson Vial
- br.ui:createSpinner(section, "Crimson Vial", 80, 0, 100, 5, "|cffFFBB00Health Percentage to use at.")
+ -- Combat Readiness
+ br.ui:createSpinner(section, "Combat Readiness", 40, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
-- Evasion
- br.ui:createSpinner(section, "Evasion", 40, 0, 100, 5, "|cffFFBB00Health Percentage to use at.")
+ br.ui:createSpinner(section, "Evasion", 35, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
-- Feint
- br.ui:createSpinner(section, "Feint", 50, 0, 100, 5, "|cffFFBB00Health Percentage to use at.")
- -- Kidney Shot
- br.ui:createSpinner(section, "Kidney Shot Defensive", 30, 0, 100, 5, "|cffFFBB00Health Percentage to use at.")
+ br.ui:createSpinner(section, "Feint", 50, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ -- Recuperate
+ br.ui:createSpinner(section, "Recuperate", 60, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
br.ui:checkSectionState(section)
-------------------------
--- INTERRUPT OPTIONS ---
@@ -159,29 +132,26 @@ local createOptions = function()
br.ui:createCheckbox(section, "Kick")
-- Kidney Shot
br.ui:createCheckbox(section, "Kidney Shot")
- -- Cheap Shot
- br.ui:createCheckbox(section, "Cheap Shot")
- -- Blind
- br.ui:createCheckbox(section, "Blind")
+ -- Gouge
+ br.ui:createCheckbox(section, "Gouge")
-- Interrupt Percentage
- br.ui:createSpinnerWithout(section, "Interrupt At", 0, 0, 95, 5, "|cffFFBB00Cast Percentage to use at.")
+ br.ui:createSpinnerWithout(section, "Interrupt At", 0, 0, 95, 5,
+ "|cffFFFFFFCast Percentage to use at. (0 is random)")
br.ui:checkSectionState(section)
----------------------
--- TOGGLE OPTIONS ---
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 4)
- --Cooldown Key Toggle
- br.ui:createDropdownWithout(section, "Cooldown Mode", br.dropOptions.Toggle, 3)
- --Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
+ -- Cooldown Key Toggle
+ br.ui:createDropdownWithout(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 3)
+ -- Defensive Key Toggle
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupts Key Toggle
- br.ui:createDropdownWithout(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
- -- Shadow Dance Toggle
- br.ui:createDropdownWithout(section, "ShadowDance Mode", br.dropOptions.Toggle, 6)
- -- Pick Pocket Toggle
- br.ui:createDropdownWithout(section, "PickPocket Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
+ -- Pick Pocket Key Toggle
+ br.ui:createDropdownWithout(section, "Pick Pocket Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -195,6 +165,7 @@ end
--- Locals ---
--------------
-- BR API Locals
+local actionList = {}
local buff
local cast
local cd
@@ -206,78 +177,383 @@ local energy
local equiped
local items
local module
+local spell
local talent
+local ui
local unit
local units
-local ui
local use
local var = {}
-- General Locals
var.profileStop = false
-
--- Variables
-var.pickPocketUnit = "player"
-var.danseBackstab = false
-var.danseBlackPowder = false
-var.danseEviscerate = false
-var.danseGloomblade = false
-var.danseRupture = false
-var.danseShadowstrike = false
-var.danseShurikenStorm = false
+var.stealthed = false
+var.openerCast = false
+var.openerTime = nil
+var.openerAttemptTime = nil
+var.getTime = 0
+-- Pick Pocket Locals
+var.pickPocketAttempted = {} -- Table to track attempted units by GUID
+var.pickPocketSuccess = {} -- Table to track successful Pick Pockets by GUID
+var.pickPocketFailed = {} -- Table to track failed Pick Pockets by GUID
+var.pickPocketCastTime = 0 -- Time when Pick Pocket was last cast
+var.pickPocketLastGUID = nil -- GUID of last Pick Pocket target for validation
-----------------
--- Functions ---
-----------------
+local function isStealthed()
+ return buff.stealth.exists() or buff.vanish.exists() or buff.shadowDance.exists() or buff.shadowmeld.exists()
+end
-var.isPicked = function(thisUnit) -- Pick Pocket Testing
- if thisUnit == nil then thisUnit = "target" end
- if (br.unpickable or unit.level() < 24
- or ui.mode.pickPocket == 3 or unit.isDummy(thisUnit) or (br._G.LootFrame:IsShown() and br._G.GetNumLootItems() == 0))
- then
- var.pickPocketUnit = thisUnit
- return true
+-- Pick Pocket Helper Functions
+local function getPickPocketMode()
+ return br.data.settings[br.loader.selectedSpec].toggles["PickPocket"] or 3
+end
+
+local function canPickPocket(thisUnit)
+ thisUnit = thisUnit or "target"
+
+ -- Check if unit exists and is valid
+ if not unit.exists(thisUnit) or not unit.valid(thisUnit) then
+ return false
end
- return false
+
+ -- Must be stealthed
+ if not var.stealthed then
+ return false
+ end
+
+ -- Get unit GUID
+ local guid = br._G.UnitGUID(thisUnit)
+ if not guid then
+ return false
+ end
+
+ -- Check if already attempted and failed
+ if var.pickPocketFailed[guid] then
+ return false
+ end
+
+ -- Check if already successfully picked
+ if var.pickPocketSuccess[guid] then
+ return false
+ end
+
+ -- Must be enemy
+ if not unit.enemy(thisUnit) then
+ return false
+ end
+
+ -- Can't pickpocket players
+ if unit.player(thisUnit) then
+ return false
+ end
+
+ -- Must be in range
+ if unit.distance(thisUnit) > 10 then
+ return false
+ end
+
+ return true
end
-var.autoStealth = function()
- for i = 1, #enemies.yards20nc do
- local thisUnit = enemies.yards20nc[i]
- if unit.reaction(thisUnit) < 4 then return true end
+local function markPickPocketResult(thisUnit, success, lootMsg, errorMsg)
+ local guid = br._G.UnitGUID(thisUnit)
+ if not guid then return end
+
+ if success then
+ var.pickPocketSuccess[guid] = true
+ var.pickPocketFailed[guid] = nil
+ -- Extract item links from loot message for display
+ local itemInfo = ""
+ if lootMsg then
+ -- Extract all item links [Item Name]
+ for itemLink in lootMsg:gmatch("%[.-%]") do
+ itemInfo = itemInfo .. " " .. itemLink
+ end
+ -- If no item links, check if it's a money message
+ if itemInfo == "" and lootMsg:find("%d") then
+ itemInfo = " (Money)"
+ end
+ end
+ -- Use br.player.ui directly since this may be called from event handler
+ if br.player and br.player.ui then
+ br.player.ui.debug("|cff00FF00[Pick Pocket]|r Success" .. itemInfo)
+ end
+ else
+ var.pickPocketFailed[guid] = true
+ var.pickPocketAttempted[guid] = nil
+ -- Use br.player.ui and br.player.unit directly since this may be called from event handler
+ if br.player and br.player.ui then
+ if errorMsg then
+ br.player.ui.debug("|cffFF0000[Pick Pocket]|r Failed: " .. errorMsg)
+ else
+ local unitName = br.player.unit and br.player.unit.name(thisUnit) or thisUnit
+ br.player.ui.debug("|cffFF0000[Pick Pocket]|r Failed on " .. unitName)
+ end
+ end
end
- return false
end
+-- Event handler for Pick Pocket detection
+local pickPocketFrame = br._G.CreateFrame("Frame")
+pickPocketFrame:RegisterEvent("UI_ERROR_MESSAGE")
+pickPocketFrame:RegisterEvent("CHAT_MSG_LOOT")
+pickPocketFrame:RegisterEvent("CHAT_MSG_MONEY")
+pickPocketFrame:SetScript("OnEvent", function(self, event, ...)
+ if event == "UI_ERROR_MESSAGE" then
+ local errorType, msg = ...
+ local currentTime = br._G.GetTime()
+
+ -- DEBUG: Log all errors to see what's coming through
+ local errorString = br._G.GetGameMessageInfo(errorType)
+
+ -- DEBUG: Try direct print to avoid br.player.ui issues
+ -- pcall(function()
+ -- if br.player and br.player.ui then
+ -- br.player.ui.debug("[PP Event] Error: " .. tostring(errorString) .. " | Type: " .. tostring(errorType) .. " | Msg: " .. tostring(msg))
+ -- end
+ -- end)
+
+ -- Try printing directly to see if br.player.ui is the issue
+ -- pcall(print, "[PP Event DEBUG] CastTime:", var.pickPocketCastTime, "CurrentTime:", currentTime) -- Only process errors that occurred shortly after Pick Pocket cast
+ -- Extended window to 2.0s to account for GCD + network delay
+ -- AND verify we actually have a Pick Pocket in flight
+ if var.pickPocketCastTime > 0 and (currentTime - var.pickPocketCastTime) < 2.0
+ and var.pickPocketLastGUID then
+
+ -- pcall(function()
+ -- br.player.ui.debug("[PP Event] Within time window. Time since cast: " .. tostring(currentTime - var.pickPocketCastTime))
+ -- end)
+
+ -- Verify the target is still the same one we tried to Pick Pocket
+ local currentGUID = br._G.UnitGUID("target")
+ if currentGUID ~= var.pickPocketLastGUID then
+ -- Target changed, ignore this error
+ -- pcall(function()
+ -- br.player.ui.debug("[PP Event] Target changed, ignoring")
+ -- end)
+ return
+ end
+
+ -- pcall(function()
+ -- br.player.ui.debug("[PP Event] Checking error type: " .. tostring(errorString))
+ -- end)
+
+ -- Use language-agnostic error type IDs
+ -- ERR_SPELL_FAILED_S = Generic spell failure (covers "no pockets to pick")
+ -- ERR_ALREADY_PICKPOCKETED = Already pickpocketed this target
+ -- ERR_NO_LOOT = Target has no loot (empty pockets)
+ -- SPELL_FAILED_BAD_TARGETS = Invalid target
+ -- SPELL_FAILED_TARGET_ENEMY = Must be an enemy
+
+ if errorString == "ERR_SPELL_FAILED_S" or
+ errorString == "ERR_ALREADY_PICKPOCKETED" or
+ errorString == "ERR_NO_LOOT" or
+ errorString == "SPELL_FAILED_BAD_TARGETS" or
+ errorString == "SPELL_FAILED_TARGET_ENEMY" then
+ -- msg contains the actual localized error message shown to player
+ -- pcall(print, "[PP Event] Calling markPickPocketResult with error:", msg)
+ markPickPocketResult("target", false, nil, msg)
+ var.pickPocketCastTime = 0
+ var.pickPocketLastGUID = nil
+ end
+ end
+ elseif event == "CHAT_MSG_LOOT" or event == "CHAT_MSG_MONEY" then
+ local msg = ...
+ local currentTime = br._G.GetTime()
+
+ -- DEBUG: Print to see if we're catching money events
+ -- pcall(print, "[PP Event] Loot/Money:", event, "Msg:", msg, "CastTime:", var.pickPocketCastTime, "GetTime:", currentTime)
+
+ -- Detect successful Pick Pocket by loot message (language-agnostic item link detection)
+ -- Or money message (CHAT_MSG_MONEY fires when you get gold/silver/copper)
+ if msg and var.pickPocketCastTime > 0 and (currentTime - var.pickPocketCastTime) < 2.0
+ and var.pickPocketLastGUID then
+ -- pcall(print, "[PP Event] Conditions met - calling success")
+ -- Check if message contains item links [Item Name] OR if it's a money event
+ if msg:find("%[.+%]") or event == "CHAT_MSG_MONEY" then
+ markPickPocketResult("target", true, msg)
+ var.pickPocketCastTime = 0
+ var.pickPocketLastGUID = nil
+ else
+ -- pcall(print, "[PP Event] No item link found and not money event")
+ end
+ else
+ -- pcall(print, "[PP Event] Conditions NOT met - CastTime:", var.pickPocketCastTime, "Diff:", currentTime - var.pickPocketCastTime, "GUID:", var.pickPocketLastGUID)
+ end
+ end
+end)
+
--------------------
--- Action Lists ---
--------------------
-local actionList = {}
+-- Action List - Pick Pocket
+actionList.PickPocket = function()
+ local ppMode = getPickPocketMode()
+
+ -- Mode 3 = Off, skip entirely
+ if ppMode == 3 then
+ return false
+ end
+
+ -- Must have a target
+ if not unit.valid("target") then
+ return false
+ end
+
+ -- Mode 2 = Only (Sap > Pick Pocket > Clear Target)
+ if ppMode == 2 then
+ -- Must be stealthed for "Only" mode
+ if not var.stealthed then
+ if ui.checked("Auto Stealth") and cast.able.stealth() and not unit.resting() then
+ if cast.stealth() then
+ ui.debug("[Pick Pocket] Entering Stealth")
+ return true
+ end
+ end
+ return false
+ end
+
+ -- Get target GUID for tracking
+ local guid = br._G.UnitGUID("target")
+ if not guid then
+ return false
+ end
+
+ -- Check if we've already processed this target (success or failure)
+ if var.pickPocketSuccess[guid] or var.pickPocketFailed[guid] then
+ -- Wait brief moment to see loot (if successful)
+ if cast.last.pickPocket(1) and cast.timeSinceLast.pickPocket() < 1.5 then
+ return true
+ end
+ -- Clear target and stop
+ br._G.ClearTarget()
+ ui.debug("[Pick Pocket] Complete - Target cleared")
+ return true
+ end
+
+ -- Check if we just cast Pick Pocket, wait for result
+ if cast.last.pickPocket(1) and cast.timeSinceLast.pickPocket() < 1.5 then
+ return true
+ end
+
+ -- Check if Pick Pocket is in-flight waiting for event (timeout after 3 seconds)
+ if var.pickPocketCastTime > 0 and var.pickPocketLastGUID then
+ local waitTime = var.getTime - var.pickPocketCastTime
+ if waitTime < 3.0 then
+ -- Still waiting for success/failure event
+ return true
+ else
+ -- Timeout - no event received, consider it failed
+ ui.debug("[Pick Pocket] Timeout waiting for event - clearing state")
+ var.pickPocketCastTime = 0
+ var.pickPocketLastGUID = nil
+ end
+ end
+
+ -- Check if target is sapped
+ local isSapped = debuff.sap.exists("target")
+ local sapAble = cast.able.sap("target")
+ local sapRefresh = debuff.sap.refresh("target")
+
+ -- PRIORITY 1: Cast Sap if needed and available (only if we CAN cast it)
+ if (not isSapped) and sapAble and sapRefresh then
+ if cast.sap("target") then
+ ui.debug("[Pick Pocket] Casting Sap")
+ return true
+ end
+ end
+
+ -- PRIORITY 2: If target is sapped OR we can't cast Sap anymore (meaning it's in flight/on CD), try Pick Pocket
+ if isSapped or (not sapAble) then
+ -- Check range before attempting Pick Pocket (Pick Pocket has 5yd range, Sap has 10yd)
+ local targetDistance = unit.distance("target")
+
+ if targetDistance > 5 then
+ -- Too far for Pick Pocket, move closer
+ -- Target is sapped so we have time to approach safely
+ -- ui.debug("[Pick Pocket] Moving closer (" .. string.format("%.1f", targetDistance) .. "yd)")
+ return true -- Wait and let movement happen
+ end
+
+ if canPickPocket("target") then
+ if cast.able.pickPocket("target") then
+ -- Set timing BEFORE attempting cast so event handler can match the error
+ var.pickPocketCastTime = br._G.GetTime()
+ var.pickPocketLastGUID = guid
+ var.pickPocketAttempted[guid] = true
+
+ -- DEBUG: Verify timing is set
+ ui.debug("[Pick Pocket] Setting CastTime to: " .. var.pickPocketCastTime)
+
+ if cast.pickPocket("target") then
+ ui.debug("[Pick Pocket] Attempting Pick Pocket")
+ else
+ -- cast.pickPocket returned false, but game may have still attempted it
+ -- Keep timing data so event handler can process the error
+ ui.debug("[Pick Pocket] Cast attempt - waiting for result")
+ end
+
+ -- Wait for result (success or failure event)
+ return true
+ end
+ -- If cast.able is false, just wait (likely on GCD from Sap)
+ return true
+ end
+ end
+
+ return false
+ end -- Mode 1 = Auto (Pick Pocket before engaging, minimal delay)
+ if ppMode == 1 then
+ -- Only attempt if stealthed and not in combat
+ if not var.stealthed or unit.inCombat() then
+ return false
+ end
+
+ -- Check if we can and should Pick Pocket
+ if canPickPocket("target") and cast.able.pickPocket("target") then
+ local targetGUID = br._G.UnitGUID("target")
+ var.pickPocketCastTime = br._G.GetTime()
+ var.pickPocketLastGUID = targetGUID
+ var.pickPocketAttempted[targetGUID] = true
+ if cast.pickPocket("target") then
+ ui.debug("Casting Pick Pocket [Auto Mode] on " .. unit.name("target"))
+ return true
+ end
+ end
+
+ -- If we just cast Pick Pocket, wait very briefly for result
+ if cast.last.pickPocket(1) and cast.timeSinceLast.pickPocket() < 0.3 then
+ return true
+ end
+
+ -- Allow rotation to continue after brief delay
+ return false
+ end
+
+ return false
+end -- End Action List - Pick Pocket
+
-- Action List - Extras
actionList.Extras = function()
-- Dummy Test
if ui.checked("DPS Testing") then
if unit.exists("target") then
- if unit.combatTime() >= (tonumber(ui.value("DPS Testing")) * 60) and unit.isDummy("target") then
+ if unit.combatTime() >= (tonumber(ui.value("DPS Testing")) * 60) and unit.isDummy() then
unit.stopAttack()
unit.clearTarget()
- ui.chatOverlay(tonumber(ui.value("DPS Testing")) .. " Minute Dummy Test Concluded - Profile Stopped")
+ ui.print(tonumber(ui.value("DPS Testing")) .. " Minute Dummy Test Concluded - Profile Stopped")
var.profileStop = true
end
end
end
- -- Tricks of the Trade
- if ui.checked("Tricks of the Trade on Focus") and cast.able.tricksOfTheTrade("focus") and unit.inCombat() and unit.exists("focus") and unit.friend("focus") then
- if cast.tricksOfTheTrade("focus") then
- ui.debug("Casting Tricks of the Trade")
- return true
- end
- end
end -- End Action List - Extras
--- Action List - Defensives
+-- Action List - Defensive
actionList.Defensive = function()
- if ui.useDefensive() and not var.stealthAll then
+ if ui.useDefensive() and not var.stealthed then
-- Cloak of Shadows
if ui.checked("Cloak of Shadows") and cast.able.cloakOfShadows() and cast.dispel.cloakOfShadows("player") then
if cast.cloakOfShadows() then
@@ -285,31 +561,35 @@ actionList.Defensive = function()
return true
end
end
- -- Crimson Vial
- if ui.checked("Crimson Vial") and cast.able.crimsonVial() and unit.hp() < ui.value("Crimson Vial") and not buff.shadowDance.exists() then
- if cast.crimsonVial() then
- ui.debug("Casting Crimson Vial")
- return true
- end
- end
-- Evasion
- if ui.checked("Evasion") and cast.able.evasion() and unit.hp() < ui.value("Evasion") and unit.inCombat() then
+ if ui.checked("Evasion") and cast.able.evasion() and unit.hp() <= ui.value("Evasion") and unit.inCombat() then
if cast.evasion() then
ui.debug("Casting Evasion")
return true
end
end
-- Feint
- if ui.checked("Feint") and unit.hp() <= ui.value("Feint") and unit.inCombat() and not buff.feint.exists() and not buff.shadowDance.exists() then
+ if ui.checked("Feint") and cast.able.feint() and unit.hp() <= ui.value("Feint") and unit.inCombat() and not buff.feint.exists() then
if cast.feint() then
ui.debug("Casting Feint")
return true
end
end
- -- Kidney Shot
- if ui.checked("Kidney Shot Defensive") and unit.hp() <= ui.value("Kidney Shot Defensive") and unit.inCombat() and not buff.shadowDance.exists() then
- if cast.kidneyShot() then
- ui.debug("Casting Kidney Shot [Defensive]")
+ -- Combat Readiness
+ if ui.checked("Combat Readiness") and cast.able.combatReadiness() and unit.hp() <= ui.value("Combat Readiness")
+ and unit.inCombat() and not buff.combatReadiness.exists()
+ then
+ if cast.combatReadiness() then
+ ui.debug("Casting Combat Readiness")
+ return true
+ end
+ end
+ -- Recuperate
+ if ui.checked("Recuperate") and cast.able.recuperate() and unit.hp() <= ui.value("Recuperate")
+ and comboPoints() > 0 and not buff.recuperate.exists()
+ then
+ if cast.recuperate() then
+ ui.debug("Casting Recuperate")
return true
end
end
@@ -318,825 +598,573 @@ end -- End Action List - Defensive
-- Action List - Interrupts
actionList.Interrupts = function()
- if ui.useInterrupt() and not var.stealthAll then
- for i = 1, #enemies.yards20 do
- local thisUnit = enemies.yards20[i]
+ if ui.useInterrupt() and not var.stealthed then
+ for i = 1, #enemies.yards10 do
+ local thisUnit = enemies.yards10[i]
local distance = unit.distance(thisUnit)
if unit.interruptable(thisUnit, ui.value("Interrupt At")) then
-- Kick
- -- kick
if ui.checked("Kick") and cast.able.kick(thisUnit) and distance < 5 then
if cast.kick(thisUnit) then
- ui.debug("Kick")
+ ui.debug("Casting Kick on " .. unit.name(thisUnit))
return true
end
end
-- Kidney Shot
- if ui.checked("Kidney Shot") and cast.able.kidneyShot(thisUnit) and cd.kick.remain() ~= 0 and cd.blind.remain() == 0 then
+ if ui.checked("Kidney Shot") and cast.able.kidneyShot(thisUnit) and comboPoints() > 0 and distance < 5 then
if cast.kidneyShot(thisUnit) then
- ui.debug("Kidney Shot")
+ ui.debug("Casting Kidney Shot on " .. unit.name(thisUnit))
return true
end
end
- -- Blind
- if ui.checked("Blind") and cast.able.blind(thisUnit) and not buff.shadowDance.exists()
- and (cd.kick.remain() ~= 0 or distance >= 5) and not buff.shadowDance.exists()
- then
- if cast.blind(thisUnit) then
- ui.debug("Blind")
- return true
- end
- end
- -- Cheap Shot
- if ui.checked("Cheap Shot") and cast.able.cheapShot(thisUnit) and buff.shadowDance.exists() and distance < 5
- and cd.kick.remain() ~= 0 and cd.kidneyShot.remain() == 0 and cd.blind.remain() == 0
- then
- if cast.cheapShot(thisUnit) then
- ui.debug("Cheap Shot")
+ -- Gouge
+ if ui.checked("Gouge") and cast.able.gouge(thisUnit) and distance < 5 then
+ if cast.gouge(thisUnit) then
+ ui.debug("Casting Gouge on " .. unit.name(thisUnit))
return true
end
end
end
end
- end -- End Interrupt and No Stealth Check
-end -- End Action List - Interrupts
+ end
+end -- End Action List - Interrupts
-- Action List - Cooldowns
actionList.Cooldowns = function()
- if unit.exists(units.dyn5) and unit.distance(units.dyn5) < 5 then
- -- Remix - Oblivion Sphere
- if ui.useCDs() and cast.able.id(435313, "target", "ground", 1, 15) and not unit.moving("target") and not var.stealthAll then
- if cast.id(435313, "target", "ground", 1, 15) then
- ui.debug("Casting Oblivion Sphere [Cds]")
- return true
- end
- end
- -- Variable - Trinket Conditions
- -- variable,name=trinket_conditions,value=(!equipped.witherbarks_branch|equipped.witherbarks_branch&cooldown.witherbarks_branch.remains<=8|equipped.bandolier_of_twisted_blades|talent.invigorating_shadowdust)
- var.trinketConditions = ((not equiped.witherbarksBranch() or equiped.witherbarksBranch() and cd.witherbarksBranch.remains() <= 8
- or equiped.bandolierOfTwistedBlades() or talent.invigoratingShadowdust))
- -- Cold Blood
- -- cold_blood,if=!talent.secret_technique&combo_points>=5
- if ui.alwaysCdNever("Cold Blood") and cast.able.coldBlood() and not talent.secretTechnique and comboPoints() >= 5 then
- if cast.coldBlood() then
- ui.debug("Casting Cold Blood [Cds]")
- return true
- end
- end
- -- Sepsis
- -- sepsis,if=variable.snd_condition&target.time_to_die>=16&(buff.perforated_veins.up|!talent.perforated_veins)
- if ui.alwaysCdNever("Sepsis") and cast.able.sepsis()
- and ((var.sndCondition and unit.ttd(units.dyn5) >= 16 and (buff.perforatedVeins.exists() or not talent.perforatedVeins)))
- then
- if cast.sepsis() then
- ui.debug("Casting Sepsis [Cds]")
- return true
- end
- end
- -- Flagellation
- -- flagellation,target_if=max:target.time_to_die,if=variable.snd_condition&combo_points>=5&target.time_to_die>10&(variable.trinket_conditions&cooldown.shadow_blades.remains<=3|fight_remains<=28|cooldown.shadow_blades.remains>=14&talent.invigorating_shadowdust&talent.shadow_dance)&(!talent.invigorating_shadowdust|talent.sepsis|!talent.shadow_dance|talent.invigorating_shadowdust.rank=2&spell_targets.shuriken_storm>=2|cooldown.symbols_of_death.remains<=3|buff.symbols_of_death.remains>3)
- if ui.alwaysCdAoENever("Flagellation", 2, #enemies.yards10) and cast.able.flagellation(var.maxTTDUnit) and ((var.sndCondition and comboPoints() >= 5 and unit.ttd(units.dyn5) > 10
- and (var.trinketConditions and cd.shadowBlades.remains() <= 3 or unit.ttdGroup(40) <= 28 or cd.shadowBlades.remains() >= 14
- and talent.invigoratingShadowdust and talent.shadowDance) and (not talent.invigoratingShadowdust
- or talent.sepsis or not talent.shadowDance or talent.rank.invigoratingShadowdust == 2
- and ui.useAOE(10, 2) or cd.symbolsOfDeath.remains() <= 3 or buff.symbolsOfDeath.remains() > 3)))
- then
- if cast.flagellation(var.maxTTDUnit) then
- ui.debug("Casting Flagellation [Cds]")
- return true
- end
- end
- -- Symbols of Death
- -- symbols_of_death,if=variable.snd_condition&(!buff.the_rotten.up|!set_bonus.tier30_2pc)&buff.symbols_of_death.remains<=3&(!talent.flagellation|cooldown.flagellation.remains>10|buff.shadow_dance.remains>=2&talent.invigorating_shadowdust|cooldown.flagellation.up&combo_points>=5&!talent.invigorating_shadowdust)
- if ui.alwaysCdNever("Symbols of Death") and cast.able.symbolsOfDeath("player") and ((var.sndCondition and (not buff.theRotten.exists() or not equiped.tier(30, 2))
- and buff.symbolsOfDeath.remains() <= 3 and (not talent.flagellation or cd.flagellation.remains() > 10 or buff.shadowDance.remains() >= 2
- and talent.invigoratingShadowdust or not cd.flagellation.exists() and comboPoints() >= 5 and not talent.invigoratingShadowdust)))
- then
- if cast.symbolsOfDeath("player") then
- ui.debug("Casting Symbols Of Death [Cds]")
- return true
- end
- end
- -- Shadow Blades
- -- shadow_blades,if=variable.snd_condition&(combo_points<=1|set_bonus.tier31_4pc)&(buff.flagellation_buff.up|buff.flagellation_persist.up|!talent.flagellation)
- if ui.alwaysCdAoENever("Shadow Blades", 3, #enemies.yards10) and cast.able.shadowBlades() and ((var.sndCondition and (comboPoints() <= 1 or equiped.tier(31, 4))
- and (buff.flagellation.exists() or buff.flagellationPersist.exists() or not talent.flagellation)))
- then
- if cast.shadowBlades() then
- ui.debug("Casting Shadow Blades [Cds]")
- return true
- end
- end
- -- Echoing Reprimand
- -- echoing_reprimand,if=variable.snd_condition&combo_points.deficit>=3
- if ui.alwaysCdNever("Echoing Reprimand") and cast.able.echoingReprimand() and var.sndCondition and comboPoints.deficit() >= 3 then
- if cast.echoingReprimand() then
- ui.debug("Casting Echoing Reprimand [Cds]")
- return true
- end
- end
- -- Shuriken Tornado
- -- shuriken_tornado,if=variable.snd_condition&buff.symbols_of_death.up&combo_points<=2&!buff.premeditation.up&(!talent.flagellation|cooldown.flagellation.remains>20)&spell_targets.shuriken_storm>=3
- if ui.alwaysCdAoENever("Shuriken Tornado", 3, #enemies.yards10) and cast.able.shurikenTornado("player", "aoe", 1, 10)
- and ((var.sndCondition and buff.symbolsOfDeath.exists() and comboPoints() <= 2 and not buff.premeditation.exists()
- and (not talent.flagellation or cd.flagellation.remains() > 20) and ui.useAOE(10, 3)))
- then
- if cast.shurikenTornado("player", "aoe", 1, 10) then
- ui.debug("Casting Shuriken Tornado - Low Combo [Cds]")
- return true
- end
- end
- -- shuriken_tornado,if=variable.snd_condition&!buff.shadow_dance.up&!buff.flagellation_buff.up&!buff.flagellation_persist.up&!buff.shadow_blades.up&spell_targets.shuriken_storm<=2&!raid_event.adds.up
- if ui.alwaysCdAoENever("Shuriken Tornado", 3, #enemies.yards10) and cast.able.shurikenTornado("player", "aoe", 1, 10) and var.sndCondition and not buff.shadowDance.exists()
- and not buff.flagellation.exists() and not buff.flagellationPersist.exists() and not buff.shadowBlades.exists() and ui.useST(10, 3)
- then
- if cast.shurikenTornado("player", "aoe", 1, 10) then
- ui.debug("Casting Shuriken Tornado [Cds]")
- return true
- end
- end
- -- Shadow Dance
- -- shadow_dance,if=!buff.shadow_dance.up&fight_remains<=8+talent.subterfuge.enabled
- if ui.mode.shadowDance == 1 and ui.alwaysCdAoENever("Shadow Dance", 3, #enemies.yards10) and cast.able.shadowDance()
- and not buff.shadowDance.exists() and unit.ttdGroup(40) <= 8 + var.subterfuge
- then
- if cast.shadowDance() then
- ui.debug("Casting Shadow Dance [Cds]")
- return true
- end
- end
- -- Goremaws Bite
- -- goremaws_bite,if=variable.snd_condition&combo_points.deficit>=3&(!cooldown.shadow_dance.up|talent.shadow_dance&buff.shadow_dance.up&!talent.invigorating_shadowdust|spell_targets.shuriken_storm<4&!talent.invigorating_shadowdust|talent.the_rotten|raid_event.adds.up)
- if ui.alwaysCdNever("Echoing Reprimand") and cast.able.goremawsBite() and ((var.sndCondition and comboPoints.deficit() >= 3
- and (not not cd.shadowDance.exists() or talent.shadowDance and buff.shadowDance.exists() and not talent.invigoratingShadowdust or ui.useST(10, 4)
- and not talent.invigoratingShadowdust or talent.theRotten)))
- then
- if cast.goremawsBite() then
- ui.debug("Casting Goremaws Bite [Cds]")
- return true
- end
- end
- -- Thistle Tea
- -- thistle_tea,if=!buff.thistle_tea.up&cooldown.thistle_tea.charges_fractional>=2.5&buff.shadow_dance.remains>=4
- if ui.alwaysCdNever("Thistle Tea") and cast.able.thistleTea() and not buff.thistleTea.exists()
- and charges.thistleTea.frac() >= 2.5 and buff.shadowDance.remains() >= 4
- then
- if cast.thistleTea() then
- ui.debug("Casting Thistle Tea - Near Cap [Cds]")
- return true
- end
- end
- -- Thistle Tea
- -- thistle_tea,if=!buff.thistle_tea.up&buff.shadow_dance.remains>=4&cooldown.secret_technique.remains<=10
- if ui.alwaysCdNever("Thistle Tea") and cast.able.thistleTea() and not buff.thistleTea.exists() and buff.shadowDance.remains() >= 4 and talent.secretTechnique and cd.secretTechnique.remains() <= 10 then
- if cast.thistleTea() then
- ui.debug("Casting Thistle Tea - Secret Technique Ends Soon [Cds]")
- return true
- end
- end
- -- Thistle Tea
- -- thistle_tea,if=!buff.thistle_tea.up&(energy.deficit>=(100)|!buff.thistle_tea.up&fight_remains<=(6*cooldown.thistle_tea.charges))&(cooldown.symbols_of_death.remains>=3|buff.symbols_of_death.up)&combo_points.deficit>=2
- if ui.alwaysCdNever("Thistle Tea") and cast.able.thistleTea() and ((not buff.thistleTea.exists() and (energy.deficit() >= (100) or not buff.thistleTea.exists()
- and unit.ttdGroup(40) <= (6 * charges.thistleTea.count())) and (cd.symbolsOfDeath.remains() >= 3
- or buff.symbolsOfDeath.exists()) and comboPoints.deficit() >= 2))
- then
- if cast.thistleTea() then
- ui.debug("Casting Thistle Tea [Cds]")
- return true
- end
- end
- -- Module - Combatpotion Up
- -- potion,if=buff.bloodlust.react|fight_remains<30|buff.symbols_of_death.up&(buff.shadow_blades.up|cooldown.shadow_blades.remains<=10)
- if buff.bloodLust.exists() or unit.ttdGroup(40) < 30 or buff.symbolsOfDeath.exists() and (buff.shadowBlades.exists() or cd.shadowBlades.remains() <= 10) then
+ if ui.useCDs() and unit.exists(units.dyn5) and unit.distance(units.dyn5) < 5 then
+ -- Potion
+ -- virmens_bite_potion,if=buff.bloodlust.react|target.time_to_die<40
+ if ui.checked("Use Combat Potion") and (buff.bloodLust.exists() or unit.ttd(units.dyn5) < 40) then
module.CombatPotionUp()
end
- -- Variable - Racial Sync
- -- variable,name=racial_sync,value=buff.shadow_blades.up|!talent.shadow_blades&buff.symbols_of_death.up|fight_remains<20
- var.racialSync = (buff.shadowBlades.exists() or not talent.shadowBlades and buff.symbolsOfDeath.exists() or unit.ttdGroup(40) < 20)
-- Racial
- -- blood_fury,if=variable.racial_sync
- -- berserking,if=variable.racial_sync
- -- fireblood,if=variable.racial_sync
- -- ancestral_call,if=variable.racial_sync
- if ui.checked("Racial") and ui.useCDs() and cast.able.racial() and (var.racialSync
- and (unit.race() == "Orc" or unit.race() == "Troll" or unit.race() == "DarkIronDwarf" or unit.race() == "MagharOrc"))
- then
- if cast.racial() then
- ui.debug("Casting Racial [Cds]")
- return true
- end
- end
- -- Use Item - Irideus Fragment
- -- use_item,name=irideus_fragment,if=(buff.cold_blood.up|(!talent.danse_macabre&buff.shadow_dance.up|buff.danse_macabre.stack>=3)&!talent.cold_blood)|fight_remains<10
- if ui.useTrinkets(items.irideusFragment) and use.able.irideusFragment() and (((buff.coldBlood.exists()
- or (not talent.danseMacabre and buff.shadowDance.exists() or buff.danseMacabre.stack() >= 3)
- and not talent.coldBlood) or unit.ttdGroup(40) < 10))
- then
- if use.irideusFragment() then
- ui.debug("Using Irideus Fragment [Cds]")
- return true
- end
- end
-
- -- Use Item - Ashes Of The Embersoul
- -- use_item,name=ashes_of_the_embersoul,if=(buff.cold_blood.up|(!talent.danse_macabre&buff.shadow_dance.up|buff.danse_macabre.stack>=3)&!talent.cold_blood)|fight_remains<10
- if ui.useTrinkets(items.ashesOfTheEmbersoul) and use.able.ashesOfTheEmbersoul() and (((buff.coldBlood.exists()
- or (not talent.danseMacabre and buff.shadowDance.exists() or buff.danseMacabre.stack() >= 3)
- and not talent.coldBlood) or unit.ttdGroup(40) < 10))
- then
- if use.ashesOfTheEmbersoul() then
- ui.debug("Using Ashes Of The Embersoul [Cds]")
- return true
- end
- end
-
- -- Use Item - Witherbarks Branch
- -- use_item,name=witherbarks_branch,if=buff.flagellation_buff.up&talent.invigorating_shadowdust|buff.shadow_blades.up|equipped.bandolier_of_twisted_blades&raid_event.adds.up
- if ui.useTrinkets(items.witherbarksBranch) and use.able.witherbarksBranch() and ((buff.flagellation.exists()
- and talent.invigoratingShadowdust or buff.shadowBlades.exists() or equiped.bandolierOfTwistedBlades()))
- then
- if use.witherbarksBranch() then
- ui.debug("Using Witherbarks Branch [Cds]")
- return true
+ -- blood_fury,if=buff.shadow_dance.up
+ -- berserking,if=buff.shadow_dance.up
+ -- arcane_torrent,if=energy<60
+ if ui.checked("Use Racial") and cast.able.racial() then
+ if (unit.race() == "Orc" or unit.race() == "Troll") and buff.shadowDance.exists() then
+ if cast.racial() then
+ ui.debug("Casting Racial - Blood Fury/Berserking")
+ return true
+ end
end
- end
-
- -- Use Item - Mirror Of Fractured Tomorrows
- -- use_item,name=mirror_of_fractured_tomorrows,if=buff.shadow_dance.up&(target.time_to_die>=15|equipped.ashes_of_the_embersoul)
- if ui.useTrinkets(items.mirrorOfFracturedTomorrows) and use.able.mirrorOfFracturedTomorrows() and ((buff.shadowDance.exists()
- and (unit.ttd(units.dyn5) >= 15 or equiped.ashesOfTheEmbersoul())))
- then
- if use.mirrorOfFracturedTomorrows() then
- ui.debug("Using Mirror Of Fractured Tomorrows [Cds]")
- return true
+ if unit.race() == "BloodElf" and energy() < 60 then
+ if cast.racial() then
+ ui.debug("Casting Racial - Arcane Torrent")
+ return true
+ end
end
end
-
- -- Use Item - Beacon To The Beyond
- -- use_item,name=beacon_to_the_beyond,if=!stealthed.all&(buff.deeper_daggers.up|!talent.deeper_daggers)&(!raid_event.adds.up|!equipped.stormeaters_boon|cooldown.stormeaters_boon.remains>20)
- if ui.useTrinkets(items.beaconToTheBeyond) and use.able.beaconToTheBeyond() and ((not var.stealthAll
- and (buff.deeperDaggers.exists() or not talent.deeperDaggers) and (not equiped.stormeatersBoon() or cd.stormeatersBoon.remains() > 20)))
- then
- if use.beaconToTheBeyond() then
- ui.debug("Using Beacon To The Beyond [Cds]")
+ -- Shadow Blades (only if available)
+ -- shadow_blades
+ if cast.able.shadowBlades() and ui.alwaysCdAoENever("Shadow Blades", 3, #enemies.yards10) then
+ if cast.shadowBlades() then
+ ui.debug("Casting Shadow Blades")
return true
end
end
-
- -- Use Item - Manic Grieftorch
- -- use_item,name=manic_grieftorch,if=!buff.shadow_blades.up&!buff.shadow_dance.up&(!cooldown.mirror_of_fractured_tomorrows.ready|!equipped.mirror_of_fractured_tomorrows)&(!cooldown.ashes_of_the_embersoul.ready|!equipped.ashes_of_the_embersoul)&(!cooldown.irideus_fragment.ready|!equipped.irideus_fragment)|fight_remains<10
- if ui.useTrinkets(items.manicGrieftorch) and use.able.manicGrieftorch() and ((not buff.shadowBlades.exists()
- and not buff.shadowDance.exists() and (not not cd.mirrorOfFracturedTomorrows.exists() or not equiped.mirrorOfFracturedTomorrows())
- and (not not cd.ashesOfTheEmbersoul.exists() or not equiped.ashesOfTheEmbersoul()) and (not not cd.irideusFragment.exists()
- or not equiped.irideusFragment()) or unit.ttdGroup(40) < 10))
- then
- if use.manicGrieftorch() then
- ui.debug("Using Manic Grieftorch [Cds]")
+ -- Premeditation (only if available)
+ -- premeditation,if=combo_points<=4
+ if cast.able.premeditation() and ui.checked("Premeditation") and buff.stealth.exists() and comboPoints() <= 4 then
+ if cast.premeditation() then
+ ui.debug("Casting Premeditation")
return true
end
end
-
-
- -- Use Item - Use Items
- -- use_items,if=!stealthed.all&(!cooldown.mirror_of_fractured_tomorrows.ready|!equipped.mirror_of_fractured_tomorrows)&(!cooldown.ashes_of_the_embersoul.ready|!equipped.ashes_of_the_embersoul)|fight_remains<10
- if ((not var.stealthAll and (not not cd.mirrorOfFracturedTomorrows.exists() or not equiped.mirrorOfFracturedTomorrows())
- and (not not cd.ashesOfTheEmbersoul.exists() or not equiped.ashesOfTheEmbersoul()) or unit.ttdGroup(40) < 10))
- then
- module.BasicTrinkets()
- end
+ -- Module - Basic Trinkets
+ module.BasicTrinkets()
end
end -- End Action List - Cooldowns
--- Action List - Stealth Cooldowns
-actionList.StealthCooldowns = function()
- -- Variable - Shd Threshold
- -- variable,name=shd_threshold,value=cooldown.shadow_dance.charges_fractional>=0.75+talent.shadow_dance
- var.shdThreshold = charges.shadowDance.frac() >= 0.75 + var.shadowDance
- -- Variable - Rotten Cb
- -- variable,name=rotten_cb,value=(!buff.the_rotten.up|!set_bonus.tier30_2pc)&(!talent.cold_blood|cooldown.cold_blood.remains<4|cooldown.cold_blood.remains>10)
- var.rottenCb = ((not buff.theRotten.exists() or not equiped.tier(30, 2)) and (not talent.coldBlood or cd.coldBlood.remains() < 4 or cd.coldBlood.remains() > 10))
- if unit.exists(units.dyn5) and unit.distance(units.dyn5) < 5 then
- -- Vanish
- -- vanish,if=(combo_points.deficit>1|buff.shadow_blades.up&talent.invigorating_shadowdust)&!variable.shd_threshold&(cooldown.flagellation.remains>=60|!talent.flagellation|fight_remains<=(30*cooldown.vanish.charges))&(cooldown.symbols_of_death.remains>3|!set_bonus.tier30_2pc)&(cooldown.secret_technique.remains>=10|!talent.secret_technique|cooldown.vanish.charges>=2&talent.invigorating_shadowdust&(buff.the_rotten.up|!talent.the_rotten)&!raid_event.adds.up)
- if ui.checked("Vanish") and ui.useCDs() and cast.able.vanish() and (((comboPoints.deficit() > 1 or buff.shadowBlades.exists()
- and talent.invigoratingShadowdust) and not var.shdThreshold and (cd.flagellation.remains() >= 60 or not talent.flagellation
- or unit.ttdGroup(40) <= (30 * charges.vanish.count())) and (cd.symbolsOfDeath.remains() > 3 or not equiped.tier(30, 2))
- and (cd.secretTechnique.remains() >= 10 or not talent.secretTechnique or charges.vanish.count() >= 2 and talent.invigoratingShadowdust
- and (buff.theRotten.exists() or not talent.theRotten))))
- then
- if cast.vanish() then
- ui.debug("Casting Vanish [Stealth Cds]")
- return true
- end
- end
- -- Shadowmeld
- -- shadowmeld,if=energy>=40&energy.deficit>=10&!variable.shd_threshold&combo_points.deficit>4
- if ui.checked("Racial") and ui.useCDs() and cast.able.racial() and unit.race() == "NightElf"
- and energy() >= 40 and energy.deficit() >= 10 and not var.shdThreshold and comboPoints.deficit() > 4
- then
- if cast.racial() then
- ui.debug("Casting Shadowmeld [Stealth Cds]")
- return true
- end
+-- Action List - Pool Resource
+actionList.Pool = function()
+ -- Preparation (only if available)
+ -- preparation,if=!buff.vanish.up&cooldown.vanish.remains>60
+ if cast.able.preparation() and ui.checked("Preparation") and not buff.vanish.exists() and cd.vanish.remains() > 60 then
+ if cast.preparation() then
+ ui.debug("Casting Preparation")
+ return true
end
end
- -- Variable - Shd Combo Points
- -- variable,name=shd_combo_points,value=combo_points.deficit>=3
- var.shdComboPoints = comboPoints.deficit() >= 3
- if unit.exists(units.dyn5) and unit.distance(units.dyn5) < 5 then
- -- Shadow Dance
- -- shadow_dance,if=(dot.rupture.ticking|talent.invigorating_shadowdust)&variable.rotten_cb&(!talent.the_first_dance|combo_points.deficit>=4|buff.shadow_blades.up)&(variable.shd_combo_points&variable.shd_threshold|(buff.shadow_blades.up|cooldown.symbols_of_death.up&!talent.sepsis|buff.symbols_of_death.remains>=4&!set_bonus.tier30_2pc|!buff.symbols_of_death.remains&set_bonus.tier30_2pc)&cooldown.secret_technique.remains<10+12*(!talent.invigorating_shadowdust|set_bonus.tier30_2pc))
- if ui.mode.shadowDance == 1 and ui.alwaysCdAoENever("Shadow Dance", 3, #enemies.yards10) and cast.able.shadowDance()
- and (((debuff.rupture.exists(units.dyn5) or talent.invigoratingShadowdust) and var.rottenCb
- and (not talent.theFirstDance or comboPoints.deficit() >= 4 or buff.shadowBlades.exists())
- and (var.shdComboPoints and var.shdThreshold or (buff.shadowBlades.exists() or not cd.symbolsOfDeath.exists()
- and not talent.sepsis or buff.symbolsOfDeath.remains() >= 4 and not equiped.tier(30, 2) or not buff.symbolsOfDeath.remains()
- and equiped.tier(30, 2)) and cd.secretTechnique.remains() < 10 + 12 * var.invigoratingTier)))
- then
- if cast.shadowDance() then
- ui.debug("Casting Shadow Dance [Stealth Cds]")
- return true
- end
- end
+ return false
+end -- End Action List - Pool
+
+-- Action List - Generator
+actionList.Generator = function()
+ -- Pool Resource Check
+ -- run_action_list,name=pool,if=buff.master_of_subtlety.down&buff.shadow_dance.down&debuff.find_weakness.down&(energy+cooldown.shadow_dance.remains*energy.regen<80|energy+cooldown.vanish.remains*energy.regen<60)
+ if not buff.masterOfSubtlety.exists() and not buff.shadowDance.exists() and not debuff.findWeakness.exists(units.dyn5)
+ and (energy() + cd.shadowDance.remains() * energy.regen() < 80 or energy() + cd.vanish.remains() * energy.regen() < 60)
+ then
+ if actionList.Pool() then return true end
end
-end -- End Action List - Stealth Cooldowns
-
--- Action List - Stealthed
-actionList.Stealthed = function()
- -- Shadowstrike
- -- shadowstrike,if=buff.stealth.up&(spell_targets.shuriken_storm<4|variable.priority_rotation)
- if cast.able.shadowstrike("target", nil, 1, ui.value("SS Range")) and ((buff.stealth.exists() and (ui.useST(10, 4) or var.priorityRotation))) then
- if cast.shadowstrike("target", nil, 1, ui.value("SS Range")) then
- ui.debug("Casting Shadowstrike [Stealthed]")
+ -- Fan of Knives
+ -- fan_of_knives,if=active_enemies>=4
+ if cast.able.fanOfKnives("player", "aoe", 1, 10) and ui.useAOE(10, 4) then
+ if cast.fanOfKnives("player", "aoe", 1, 10) then
+ ui.debug("Casting Fan of Knives")
return true
end
end
- -- Call Action List - Finish
- -- call_action_list,name=finish,if=effective_combo_points>=var.cpMaxSpend
- if var.effectiveComboPoints >= var.cpMaxSpend then
- if actionList.Finish() then return true end
- end
- -- Call Action List - Finish
- -- call_action_list,name=finish,if=buff.shuriken_tornado.up&combo_points.deficit<=2
- if buff.shurikenTornado.exists() and comboPoints.deficit() <= 2 then
- if actionList.Finish() then return true end
- end
- -- Call Action List - Finish
- -- call_action_list,name=finish,if=combo_points.deficit<=1+(talent.deeper_stratagem|talent.secret_stratagem)
- if (comboPoints.deficit() <= 1 + var.deepSecrets) then
- if actionList.Finish() then return true end
- end
- -- Backstab
- -- backstab,if=!buff.premeditation.up&buff.shadow_dance.remains>=3&buff.shadow_blades.up&!used_for_danse&talent.danse_macabre&spell_targets.shuriken_storm<=3&!buff.the_rotten.up
- if cast.able.backstab() and not buff.premeditation.exists() and buff.shadowDance.remains() >= 3
- and buff.shadowBlades.exists() and not var.danseBackstab and talent.danseMacabre and ui.useST(10, 4) and not buff.theRotten.exists()
+ -- Hemorrhage (if talented)
+ -- hemorrhage,if=remains<3|position_front
+ if cast.able.hemorrhage(units.dyn5)
+ and (debuff.hemorrhage.remains(units.dyn5) < 3 or unit.facing(units.dyn5, "player") or unit.level() < 40)
then
- if cast.backstab() then
- ui.debug("Casting Backstab [Stealthed]")
+ if cast.hemorrhage(units.dyn5) then
+ ui.debug("Casting Hemorrhage")
return true
end
end
- -- Gloomblade
- -- gloomblade,if=!buff.premeditation.up&buff.shadow_dance.remains>=3&buff.shadow_blades.up&!used_for_danse&talent.danse_macabre&spell_targets.shuriken_storm<=4
- if cast.able.gloomblade() and not buff.premeditation.exists() and buff.shadowDance.remains() >= 3 and buff.shadowBlades.exists()
- and not var.danseGloomblade and talent.danseMacabre and ui.useST(10, 5)
+ -- Shuriken Toss (if talented)
+ -- shuriken_toss,if=talent.shuriken_toss.enabled&(energy<65&energy.regen<16)
+ if talent.shurikenToss and cast.able.shurikenToss(units.dyn30)
+ and (energy() < 65 and energy.regen() < 16)
then
- if cast.gloomblade() then
- ui.debug("Casting Gloomblade [Stealthed]")
+ if cast.shurikenToss(units.dyn30) then
+ ui.debug("Casting Shuriken Toss")
return true
end
end
- -- Shadowstrike
- -- shadowstrike,if=!used_for_danse&buff.shadow_blades.up
- if cast.able.shadowstrike("target", nil, 1, ui.value("SS Range")) and not var.danseShadowstrike and buff.shadowBlades.exists() then
- if cast.shadowstrike("target", nil, 1, ui.value("SS Range")) then
- ui.debug("Casting Shadowstrike - Shadow Blades [Stealthed]")
+ -- Backstab
+ -- backstab
+ if cast.able.backstab(units.dyn5) then
+ if cast.backstab(units.dyn5) then
+ ui.debug("Casting Backstab")
return true
end
end
- -- Shuriken Storm
- -- shuriken_storm,if=!buff.premeditation.up&spell_targets>=4
- if cast.able.shurikenStorm("player", "aoe", 1, 8) and cast.able.shurikenStorm() and not buff.premeditation.exists() and ui.useAOE(10, 4) then
- if cast.shurikenStorm() then
- ui.debug("Casting Shuriken Storm [Stealthed]")
+ -- Pool
+ if actionList.Pool() then return true end
+end -- End Action List - Generator
+
+-- Action List - Finisher
+actionList.Finisher = function()
+ -- Slice and Dice
+ -- slice_and_dice,if=buff.slice_and_dice.remains<4
+ if cast.able.sliceAndDice() and buff.sliceAndDice.remains() < 4 then
+ if cast.sliceAndDice() then
+ ui.debug("Casting Slice and Dice")
return true
end
end
- -- Shadowstrike
- -- shadowstrike
- if cast.able.shadowstrike("target", nil, 1, ui.value("SS Range")) then
- if cast.shadowstrike("target", nil, 1, ui.value("SS Range")) then
- ui.debug("Casting Shadowstrike - Just Strike [Stealthed]")
+ -- Rupture (if available)
+ -- rupture,if=ticks_remain<2&active_enemies<3
+ if cast.able.rupture(units.dyn5) and debuff.rupture.ticksRemain(units.dyn5) < 2 and ui.useST(10, 3) then
+ if cast.rupture(units.dyn5) then
+ ui.debug("Casting Rupture")
return true
end
end
-end -- End Action List - Stealthed
-
--- Action List - Finish
-actionList.Finish = function()
- -- Rupture
- -- rupture,if=!dot.rupture.ticking&target.time_to_die-remains>6
- if cast.able.rupture() and not debuff.rupture.exists(units.dyn5) and unit.ttd(units.dyn5) - debuff.rupture.remain(units.dyn5) > 6 then
- if cast.rupture() then
- ui.debug("Casting Rupture [Finish]")
- return true
+ -- Crimson Tempest (if talented and available)
+ -- crimson_tempest,if=(active_enemies>1&dot.crimson_tempest_dot.ticks_remain<=2&combo_points=5)|active_enemies>=5
+ if talent.crimsonTempest and cast.able.crimsonTempest("player", "aoe", 1, 10) then
+ if (ui.useAOE(10, 2) and debuff.crimsonTempest.ticksRemain(units.dyn10AOE) <= 2 and comboPoints() == 5)
+ or ui.useAOE(10, 5)
+ then
+ if cast.crimsonTempest("player", "aoe", 1, 10) then
+ ui.debug("Casting Crimson Tempest")
+ return true
+ end
end
end
- -- Variable - Premed Snd Condition
- -- variable,name=premed_snd_condition,value=talent.premeditation.enabled&spell_targets.shuriken_storm<5
- var.premedSndCondition = talent.premeditation and ui.useST(10, 5)
- -- Slice And Dice
- -- slice_and_dice,if=!stealthed.all&!variable.premed_snd_condition&spell_targets.shuriken_storm<6&!buff.shadow_dance.up&buff.slice_and_dice.remains3&dot.crimson_tempest_dot.ticks_remain>=2)
+ if cast.able.eviscerate(units.dyn5) then
+ if ui.useST(10, 4) or (ui.useAOE(10, 4) and (not talent.crimsonTempest or debuff.crimsonTempest.ticksRemain(units.dyn10AOE) >= 2)) then
+ if cast.eviscerate(units.dyn5) then
+ ui.debug("Casting Eviscerate")
+ return true
+ end
end
end
- -- Variable - Skip Rupture
- -- variable,name=skip_rupture,value=buff.thistle_tea.up&spell_targets.shuriken_storm=1|buff.shadow_dance.up&(spell_targets.shuriken_storm=1|dot.rupture.ticking&spell_targets.shuriken_storm>=2)
- var.skipRupture = (buff.thistleTea.exists() and ui.useST(10, 2) or buff.shadowDance.exists() and (ui.useST(10, 2) or debuff.rupture.exists(units.dyn5) and ui.useAOE(10, 2)))
- -- Rupture
- -- rupture,if=(!variable.skip_rupture|variable.priority_rotation)&target.time_to_die-remains>6&refreshable
- if cast.able.rupture() and (((not var.skipRupture or var.priorityRotation)
- and unit.ttd(units.dyn5) - debuff.rupture.remain(units.dyn5) > 6 and debuff.rupture.refresh(units.dyn5)))
- then
- if cast.rupture() then
- ui.debug("Casting Rupture - Refresh [Finish]")
- return true
- end
+ -- Pool
+ if actionList.Pool() then return true end
+end -- End Action List - Finisher
+
+-- Action List - Opener
+actionList.Opener = function(thisUnit)
+ -- Use provided unit or default to units.dyn5
+ thisUnit = thisUnit or units.dyn5
+
+ -- Only run if target exists and is valid
+ -- unit.valid() handles: exists, alive, attackable, reaction checks
+ if not unit.valid(thisUnit) then
+ return false
end
- -- Rupture
- -- rupture,if=buff.finality_rupture.up&buff.shadow_dance.up&spell_targets.shuriken_storm<=4&!action.rupture.used_for_danse
- if cast.able.rupture() and buff.finalityRupture.exists() and buff.shadowDance.exists() and ui.useST(10, 4) and not var.danseRupture then
- if cast.rupture() then
- ui.debug("Casting Rupture - Finality [Finish]")
- return true
- end
+
+ -- If opener has already been cast AND we're in combat, skip
+ -- This allows the opener to retry if we're still out of combat
+ if var.openerCast and unit.inCombat() then
+ return false
end
- -- Cold Blood
- -- cold_blood,if=variable.secret_condition&cooldown.secret_technique.ready
- if ui.alwaysCdNever("Cold Blood") and cast.able.coldBlood() and (var.secretCondition or not ui.alwaysCdAoENever("Shadow Dance", 3, #enemies.yards10))
- and not cd.secretTechnique.exists()
- then
- if cast.coldBlood() then
- ui.debug("Casting Cold Blood [Finish]")
- return true
- end
+
+ -- Don't fire opener while falling/moving from Shadowstep
+ -- Wait at least 0.5 seconds after Shadowstep to ensure we're positioned behind target
+ if unit.falling() or (cast.last.shadowstep(1) and cast.timeSinceLast.shadowstep() < 0.5) then
+ return false
end
- -- Secret Technique
- -- secret_technique,if=variable.secret_condition&(!talent.cold_blood|cooldown.cold_blood.remains>buff.shadow_dance.remains-2|!talent.improved_shadow_dance)
- if ui.alwaysCdAoENever("Secret Technique", 3, #enemies.yards10) and cast.able.secretTechnique()
- and (((var.secretCondition or not ui.alwaysCdAoENever("Shadow Dance", 3, #enemies.yards10)) and (not talent.coldBlood
- or cd.coldBlood.remains() > buff.shadowDance.remains() - 2 or not talent.improvedShadowDance)))
- then
- if cast.secretTechnique() then
- ui.debug("Casting Secret Technique [Finish]")
- return true
+
+ -- Apply Slice and Dice first if missing (can be cast from stealth without breaking it)
+ if not buff.sliceAndDice.exists() and cast.able.sliceAndDice("player") and comboPoints() > 0 then
+ if cast.sliceAndDice("player") then
+ ui.debug("Casting Slice and Dice [Opener - Pre-buff]")
+ return true -- Applied, will try opener again next frame
end
end
- -- Rupture
- -- rupture,cycle_targets=1,if=!variable.skip_rupture&!variable.priority_rotation&spell_targets.shuriken_storm>=2&target.time_to_die>=(2*combo_points)&refreshable
- if cast.able.rupture() and not var.skipRupture and not var.priorityRotation and ui.useAOE(10, 2)
- and unit.ttd(units.dyn5) >= (2 * comboPoints()) and debuff.rupture.refresh(units.dyn5)
+
+ local targetDistance = unit.distance(thisUnit)
+ local behindTarget = not unit.facing(thisUnit, "player")
+ local enemyCount = #enemies.yards10 -- Count enemies for AoE decision
+ local inStealth = var.stealthed
+
+ -- ==================== RANGE CHECK & SHADOWSTEP ====================
+ -- If out of melee range and Shadowstep is available, use it first
+ if ui.checked("Shadowstep") and targetDistance >= 8 and targetDistance <= 25
+ and cast.able.shadowstep(thisUnit)
then
- if cast.rupture() then
- ui.debug("Casting Rupture - AOE [Finish]")
+ if cast.shadowstep(thisUnit) then
+ ui.debug("Shadowstep to engage " .. unit.name(thisUnit) .. " [Opener]")
return true
end
end
- -- Rupture
- -- rupture,if=!variable.skip_rupture&remainscooldown.symbols_of_death.remains+5
- if cast.able.rupture() and not var.skipRupture and debuff.rupture.remain(units.dyn5) < cd.symbolsOfDeath.remains() + 10
- and cd.symbolsOfDeath.remains() <= 5 and unit.ttd(units.dyn5) - debuff.rupture.remain(units.dyn5) > cd.symbolsOfDeath.remains() + 5
- then
- if cast.rupture() then
- ui.debug("Casting Rupture - Symbols of Death Soon [Finish]")
- return true
- end
+
+ -- If still out of melee range and can't Shadowstep, can't engage yet
+ if targetDistance >= 5 then
+ return false
end
- -- Black Powder
- -- black_powder,if=!variable.priority_rotation&spell_targets>=3
- if cast.able.blackPowder() and not var.priorityRotation and ui.useAOE(10, 3) then
- if cast.blackPowder() then
- ui.debug("Casting Black Powder [Finish]")
- return true
+
+ -- ==================== STEALTH OPENERS ====================
+ if inStealth then
+ -- Premeditation before opener
+ if cast.able.premeditation() and ui.checked("Premeditation") and comboPoints() < 5 then
+ if cast.premeditation() then
+ ui.debug("Casting Premeditation [Opener]")
+ return true
+ end
end
- end
- -- Eviscerate
- -- eviscerate
- if cast.able.eviscerate() then
- if cast.eviscerate() then
- ui.debug("Casting Eviscerate [Finish]")
- return true
+
+ -- * AoE Opener (3+ enemies) - Fan of Knives
+ if ui.useAOE(10, 3) and enemyCount >= 3 then
+ if cast.able.fanOfKnives("player", "aoe", 1, 10) then
+ if cast.fanOfKnives("player", "aoe", 1, 10) then
+ var.openerCast = true
+ var.openerTime = var.getTime
+ var.openerAttemptTime = nil
+ ui.debug("Casting Fan of Knives [Opener - AoE Stealth]")
+ return true
+ end
+ end
end
- end
-end -- End Action List - Finish
-
--- Action List - Build
-actionList.Build = function()
- -- Shuriken Storm
- -- shuriken_storm,if=spell_targets>=2+(talent.gloomblade&buff.lingering_shadow.remains>=6|buff.perforated_veins.up)
- if cast.able.shurikenStorm("player", "aoe", 1, 10) and (ui.useAOE(10, 2 + var.gloomShadowVeins)) then
- if cast.shurikenStorm("player", "aoe", 1, 10) then
- ui.debug("Casting Shuriken Storm [Build]")
- return true
+
+ -- ==================== SINGLE TARGET STEALTH OPENER ====================
+ -- Priority: Ambush (from behind) > Hemorrhage
+
+ -- * Ambush - Only from behind
+ if cast.able.ambush(thisUnit) then
+ if behindTarget then
+ -- Perfect conditions - cast Ambush
+ if cast.ambush(thisUnit) then
+ var.openerCast = true
+ var.openerTime = var.getTime
+ var.openerAttemptTime = nil
+ ui.debug("Casting Ambush on " .. unit.name(thisUnit) .. " [Opener - Stealth Behind]")
+ return true
+ end
+ -- Ambush failed to cast (could be range, GCD, etc) - don't fall through yet
+ return false
+ else
+ -- Not behind target - use smart timeout logic
+ if not var.openerAttemptTime then
+ var.openerAttemptTime = var.getTime
+ end
+
+ local openerWaitTime = var.getTime - var.openerAttemptTime
+
+ -- Dynamic timeout based on situation:
+ -- 1. If just used Shadowstep: Give extra time (1.5s) for positioning to settle
+ -- 2. If player is moving: Give more time (1.2s) - might be repositioning
+ -- 3. Base timeout: 1.0s
+ local timeoutDuration = 1.0 -- Base timeout
+
+ if cast.last.shadowstep(2) then
+ timeoutDuration = 1.5 -- Give Shadowstep time to settle
+ end
+
+ -- If player is moving, we might be trying to get behind - give more time
+ if unit.moving("player") then
+ timeoutDuration = math.max(timeoutDuration, 1.2)
+ end
+
+ if openerWaitTime < timeoutDuration then
+ -- Still waiting for positioning
+ return true
+ else
+ -- Timeout - fall through to Hemorrhage
+ var.openerAttemptTime = nil
+ end
+ end
+ elseif not cast.able.ambush(thisUnit) and not cast.last.ambush(1) then
+ -- Ambush not available (wrong level, not learned) - but don't spam if we just cast it
end
- end
- -- Gloomblade
- -- gloomblade
- if cast.able.gloomblade() then
- if cast.gloomblade() then
- ui.debug("Casting Gloomblade [Build]")
- return true
+
+ -- * Hemorrhage - Fallback when Ambush not available or positioning timeout
+ if cast.able.hemorrhage(thisUnit) then
+ if cast.hemorrhage(thisUnit) then
+ var.openerCast = true
+ var.openerTime = var.getTime
+ var.openerAttemptTime = nil
+ ui.debug("Casting Hemorrhage on " .. unit.name(thisUnit) .. " [Opener - Stealth]")
+ return true
+ end
end
- end
- -- Backstab
- -- backstab
- if cast.able.backstab() then
- if cast.backstab() then
- ui.debug("Casting Backstab [Build]")
- return true
+
+ -- * Backstab - Last resort fallback
+ if cast.able.backstab(thisUnit) and behindTarget then
+ if cast.backstab(thisUnit) then
+ var.openerCast = true
+ var.openerTime = var.getTime
+ var.openerAttemptTime = nil
+ ui.debug("Casting Backstab on " .. unit.name(thisUnit) .. " [Opener - Stealth Fallback]")
+ return true
+ end
end
end
- -- Sinister Strike
- if unit.level() < 14 and cast.able.sinisterStrike() then
- if cast.sinisterStrike() then
- ui.debug("Casting Sinister Strike [Build]")
- return true
+
+ -- ==================== NON-STEALTH OPENERS ====================
+ if not inStealth then
+ -- * AoE Non-Stealth Opener (3+ enemies)
+ if ui.useAOE(10, 3) and enemyCount >= 3 then
+ -- Fan of Knives for instant AoE damage
+ if cast.able.fanOfKnives("player", "aoe", 1, 10) then
+ if cast.fanOfKnives("player", "aoe", 1, 10) then
+ var.openerCast = true
+ var.openerTime = var.getTime
+ ui.debug("Casting Fan of Knives [Opener - AoE]")
+ return true
+ end
+ end
+ end
+
+ -- * Single Target Non-Stealth Opener
+ -- Hemorrhage - best opener for combo point + debuff
+ if cast.able.hemorrhage(thisUnit) then
+ if cast.hemorrhage(thisUnit) then
+ var.openerCast = true
+ var.openerTime = var.getTime
+ ui.debug("Casting Hemorrhage on " .. unit.name(thisUnit) .. " [Opener]")
+ return true
+ end
+ end
+
+ -- * Backstab - Fallback if behind target
+ if cast.able.backstab(thisUnit) and behindTarget then
+ if cast.backstab(thisUnit) then
+ var.openerCast = true
+ var.openerTime = var.getTime
+ ui.debug("Casting Backstab on " .. unit.name(thisUnit) .. " [Opener - Fallback]")
+ return true
+ end
+ end
+
+ -- * Sinister Strike - Last resort for low levels
+ if cast.able.sinisterStrike(thisUnit) then
+ if cast.sinisterStrike(thisUnit) then
+ var.openerCast = true
+ var.openerTime = var.getTime
+ ui.debug("Casting Sinister Strike on " .. unit.name(thisUnit) .. " [Opener - Low Level]")
+ return true
+ end
end
end
-end -- End Action List - Build
+
+ return false
+end -- End Action List - Opener
-- Action List - PreCombat
actionList.PreCombat = function()
if not unit.inCombat() and not (unit.flying() or unit.mounted()) then
- -- Poisons
- -- apply_poison
+ -- Apply Poisons
if not unit.moving() then
- if ui.value("Lethal Poison") == 1 and buff.instantPoison.remain() < 300 and not cast.last.instantPoison() and ui.timer("Leathal Poison", 1) then
+ -- Lethal Poison
+ if ui.value("Lethal Poison") == 1 and buff.instantPoison.remain() < 300 and not cast.last.instantPoison() then
if cast.instantPoison("player") then
ui.debug("Casting Instant Poison")
return true
end
end
- if ui.value("Lethal Poison") == 2 and buff.woundPoison.remain() < 300 and not cast.last.woundPoison() and ui.timer("Leathal Poison", 1) then
+ if ui.value("Lethal Poison") == 2 and buff.deadlyPoison.remain() < 300 and not cast.last.deadlyPoison() then
+ if cast.deadlyPoison("player") then
+ ui.debug("Casting Deadly Poison")
+ return true
+ end
+ end
+ if ui.value("Lethal Poison") == 3 and buff.woundPoison.remain() < 300 and not cast.last.woundPoison() then
if cast.woundPoison("player") then
ui.debug("Casting Wound Poison")
return true
end
end
- if ui.value("Non-Lethal Poison") == 1 and buff.cripplingPoison.remain() < 300 and not cast.last.cripplingPoison() and ui.timer("Non-Leathal Poison", 1) then
+ -- Non-Lethal Poison
+ if ui.value("Non-Lethal Poison") == 2 and buff.cripplingPoison.remain() < 300 and not cast.last.cripplingPoison() then
if cast.cripplingPoison("player") then
ui.debug("Casting Crippling Poison")
return true
end
end
- if ui.value("Non-Lethal Poison") == 2 and buff.numbingPoison.remain() < 300 and not cast.last.numbingPoison() and ui.timer("Non-Leathal Poison", 1) then
- if cast.numbingPoison("player") then
- ui.debug("Casting Numbing Poison")
+ if ui.value("Non-Lethal Poison") == 4 and buff.mindNumbingPoison.remain() < 300 and not cast.last.mindNumbingPoison() then
+ if cast.mindNumbingPoison("player") then
+ ui.debug("Casting Mind-Numbing Poison")
return true
end
end
- end
- -- Stealth
- -- stealth
- if ui.checked("Stealth") and cast.able.stealth() and (not unit.resting() or unit.isDummy("target")) and not var.stealthAll then
- if ui.value("Stealth") == 1 then
- if cast.stealth() then
- ui.debug("Casting Stealth [Pre-Combat]")
- return true
- end
- end
- if var.autoStealth() and ui.value("Stealth") == 3 then
- if cast.stealth() then
- ui.debug("Casting Stealth - Detected Enemy Nearby [Pre-Combat]")
+ if ui.value("Non-Lethal Poison") == 5 and buff.paralyticPoison.remain() < 300 and not cast.last.paralyticPoison() then
+ if cast.paralyticPoison("player") then
+ ui.debug("Casting Paralytic Poison")
return true
end
end
end
- -- Variable - Algethar Puzzle Box Precombat Cast
- -- variable,name=algethar_puzzle_box_precombat_cast,value=3
- var.algetharPuzzleBoxPrecombatCast = 3
- if unit.valid("target") then
- if --[[ui.checked("Pre-Pull") and ui.useCDs() and]] ui.mode.pickPocket ~= 2
- and (not unit.inCombat() or (unit.inCombat() and not cast.last.vanish())) and not buff.vanish.exists()
- then
- -- Slice and Dice
- -- slice_and_dice,precombat_seconds=1
- if cast.able.sliceAndDice() and comboPoints() > 0 and ui.pullTimer() <= 1 then
- if cast.sliceAndDice() then
- ui.debug("Casting Slice and Dice [Pre-Combat]")
- return true
- end
- end
- -- Shadowstep
- if ui.checked("Shadowstep") and cast.able.shadowstep("target")
- and (not var.isPicked("target") or not var.stealth or energy() < 40 or (ui.value("SS Range") < 25 and unit.distance("target") > ui.value("SS Range")))
- and unit.distance("target") > 10 and cast.timeSinceLast.shadowstrike() > unit.gcd(true)
- then
- if cast.shadowstep("target") then
- ui.debug("Casting Shadowstep [Pre-Combat]")
- return true
- end
- end
- if var.stealthAll then
- -- Pickpocket
- for i = 1, #enemies.yards10nc do
- local thisUnit = enemies.yards10nc[i]
- if not var.isPicked(thisUnit) and (unit.isUnit(thisUnit, "target") or ui.mode.pickPocket == 2) then
- if ui.mode.pickPocket == 2 and debuff.sap.remain(thisUnit) < 1 then
- if cast.sap(thisUnit) then
- ui.debug("Casting Sap - Pickpocket [Pre-Combat]")
- return true
- end
- end
- if cast.pickPocket(thisUnit) then
- br.pickPocketing = true
- ui.debug("Casting Pickpocket [Pre-Combat]")
- return true
- end
- end
- end
- -- Shadowstrike
- if ui.value("Stealth Breaker") == 1 then
- if cast.able.shadowstrike("target", nil, 1, ui.value("SS Range")) and (var.isPicked() or unit.distance("target") > 10) then
- if cast.shadowstrike("target", nil, 1, ui.value("SS Range")) then
- ui.debug("Casting Shadowstrike [Pre-Combat]")
- return true
- end
- end
- end
- -- Cheap Shot
- if (ui.value("Stealth Breaker") == 2 or (ui.value("Stealth Breaker") == 1 and unit.level() < 7))
- and cast.able.cheapShot("target") and cast.timeSinceLast.cheapShot() > unit.gcd(true)
- then
- if cast.cheapShot("target") then
- ui.debug("Casting Cheap Shot [Pre-Combat]")
- return true
- end
- end
- end
- -- Sinister Strike / Backstab
- if ui.value("Stealth Breaker") == 3 then
- -- Backstab
- if cast.able.backstab("target") and cast.timeSinceLast.backstab() > unit.gcd(true) then
- if cast.backstab("target") then
- ui.debug("Casting Backstab [Pre-Combat]")
- return true
- end
- end
- -- Sinister Strike
- if unit.level() < 14 and cast.able.sinisterStrike("target") and cast.timeSinceLast.sinisterStrike() > unit.gcd(true) then
- if cast.sinisterStrike("target") then
- ui.debug("Casting Sinister Strike [Pre-Combat]")
- return true
- end
- end
- end
- -- Start Attack
- if not var.stealthAll and cast.able.autoAttack("target") and energy() < 45 and unit.distance("target") < 5 then
- if cast.autoAttack("target") then
- ui.debug("Casting Auto Attack [Pre-Combat]")
- return true
- end
- end
+ -- Stealth
+ if ui.checked("Auto Stealth") and cast.able.stealth() and not unit.resting() and not var.stealthed then
+ if cast.stealth() then
+ ui.debug("Casting Stealth [Pre-Combat]")
+ return true
end
end
+ -- Pick Pocket (highest priority after stealth, before opener)
+ if actionList.PickPocket() then return true end
+ -- Call Opener if target is valid (but NOT in Pick Pocket Only mode)
+ local ppMode = getPickPocketMode()
+ if ppMode ~= 2 and unit.valid("target") then
+ if actionList.Opener("target") then return true end
+ end
end
end -- End Action List - PreCombat
-- Action List - Combat
actionList.Combat = function()
+ -- Block entire combat rotation if in Pick Pocket Only mode
+ local ppMode = getPickPocketMode()
+ if ppMode == 2 and not unit.inCombat() then
+ return false -- Don't engage in combat in Pick Pocket Only mode
+ end
+
if unit.inCombat() and not var.profileStop and unit.valid("target") then
- -- Stealth
- -- stealth
- -- if ui.checked("Stealth") and cast.able.stealth() and (not unit.resting() or unit.isDummy("target")) and not var.stealthAll then
- -- if ui.value("Stealth") == 1 then
- -- if cast.stealth() then
- -- ui.debug("Casting Stealth [Combat]")
- -- return true
- -- end
- -- end
- -- if var.autoStealth() and ui.value("Stealth") == 3 then
- -- if cast.stealth() then
- -- ui.debug("Casting Stealth - Detected Enemy Nearby [Combat]")
- -- return true
- -- end
- -- end
- -- end
- ------------------------------
- --- In Combat - Interrupts ---
- ------------------------------
- if actionList.Interrupts() then return true end
- ----------------------------------
- --- In Combat - Begin Rotation ---
- ----------------------------------
- -- Shadowstep
- if ui.checked("Shadowstep") and cast.able.shadowstep("target") and cast.able.shadowstep()
- and unit.distance("target") >= 8 and cast.timeSinceLast.shadowstrike() > unit.gcd(true)
- then
- if cast.shadowstep("target") then
- ui.debug("Casting Shadowstep [Combat]")
- return true
- end
+ -- * Opener - Must execute first
+ if actionList.Opener() then return true end
+
+ -- * Block entire rotation until opener has been cast AND landed
+ -- This prevents any abilities from firing before the engagement opener completes
+ -- Also ensure we wait at least 0.3s after opener to confirm it landed
+ if not var.openerCast or (var.openerTime and var.getTime - var.openerTime < 0.3) then
+ return true -- Wait for opener to execute and land
end
+
-- Auto Attack
-- auto_attack
- if not var.stealthAll and cast.able.autoAttack("target") and unit.distance("target") < 5 then
- if cast.autoAttack("target") then
+ if cast.able.autoAttack(units.dyn5) and unit.distance(units.dyn5) < 5 and not var.stealthed then
+ if cast.autoAttack(units.dyn5) then
ui.debug("Casting Auto Attack [Combat]")
return true
end
end
- -- Variable - Snd Condition
- -- variable,name=snd_condition,value=buff.slice_and_dice.up|spell_targets.shuriken_storm>=cp_max_spend
- var.sndCondition = (buff.sliceAndDice.exists() or ui.useAOE(10, var.cpMaxSpend))
- -- Action List - Cooldowns
- -- call_action_list,name=cds
- if actionList.Cooldowns() then return true end
- -- Slice and Dice
- -- slice_and_dice,if=spell_targets.shuriken_storm6&combo_points>=4
- if cast.able.sliceAndDice() and ui.useST(10, var.cpMaxSpend)
- and buff.sliceAndDice.remain() < unit.gcd(true) and unit.ttdGroup(40) > 6 and comboPoints() >= 4
- then
- if cast.sliceAndDice() then
- ui.debug("Casting Slice and Dice [Combat]")
+ -- Kick (Outside of Interrupt Toggle for Priority)
+ -- kick
+ if actionList.Interrupts() then return true end
+ -- Shadowstep
+ if ui.checked("Shadowstep") and cast.able.shadowstep(units.dyn5) and unit.distance(units.dyn5) > 10 then
+ if cast.shadowstep(units.dyn5) then
+ ui.debug("Casting Shadowstep [Combat]")
return true
end
end
- -- Action List - Stealthed
- -- run_action_list,name=stealthed,if=stealthed.all
- if var.stealthAll then
- if actionList.Stealthed() then return true end
- end
- -- Variable - Priority Rotation
- -- variable,name=priority_rotation,value=priority_rotation
- var.priorityRotation = ui.checked("Priority Rotation") and ui.useAOE(10, 2)
- -- Variable - Stealth Threshold
- -- variable,name=stealth_threshold,value=20+talent.vigor.rank*25+talent.thistle_tea*20+talent.shadowcraft*20
- var.stealthThreshold = 20 + var.vigor * 25 + var.thistleTea * 20 + var.shadowcraft * 20
- -- Variable - Stealth Helper
- -- variable,name=stealth_helper,value=energy>=variable.stealth_threshold
- var.stealthHelper = energy() >= var.stealthThreshold
- -- Variable - Stealth Helper
- -- variable,name=stealth_helper,value=energy.deficit<=variable.stealth_threshold,if=!talent.vigor|talent.shadowcraft
- var.stealthHelper = (not talent.vigor or talent.shadowcraft) and energy.deficit() <= var.stealthThreshold or
- false
- -- Call Action List - Stealth Cds
- -- call_action_list,name=stealth_cds,if=variable.stealth_helper|talent.invigorating_shadowdust
- if (var.stealthHelper or talent.invigoratingShadowdust) then
- if actionList.StealthCooldowns() then return true end
- end
- -- Action List - Finish
- -- call_action_list,name=finish,if=variable.effective_combo_points>=cp_max_spend
- if var.effectiveComboPoints >= var.cpMaxSpend then
- if actionList.Finish() then return true end
- end
- -- call_action_list,name=finish,if=combo_points.deficit<=1|fight_remains<=1&effective_combo_points>=3
- if (comboPoints.deficit() <= 1 or unit.ttdGroup(40) <= 1 and var.effectiveComboPoints >= 3) then
- if actionList.Finish() then return true end
- end
- -- call_action_list,name=finish,if=spell_targets.shuriken_storm>=4&variable.effective_combo_points>=4
- if ui.useAOE(10, 4) and var.effectiveComboPoints >= 4 then
- if actionList.Finish() then return true end
- end
- -- Action List - Build
- -- call_action_list,name=build,if=energy.deficit<=variable.stealth_threshold
- if energy.deficit() <= var.stealthThreshold then
- if actionList.Build() then return true end
+ -- Cooldowns
+ if actionList.Cooldowns() then return true end
+ -- Pool Resource for Ambush
+ -- pool_resource,for_next=1
+ -- ambush,if=combo_points<5|(talent.anticipation.enabled&anticipation_charges<3)|(buff.sleight_of_hand.up&buff.sleight_of_hand.remains<=gcd)
+ if var.stealthed then
+ if cast.able.ambush(units.dyn5) then
+ if comboPoints() < 5
+ or (talent.anticipation and buff.anticipation.stack() < 3)
+ or (buff.sleightOfHand.exists() and buff.sleightOfHand.remains() <= unit.gcd(true))
+ then
+ if energy() < 60 then
+ -- Pool energy
+ return true
+ end
+ if cast.ambush(units.dyn5) then
+ ui.debug("Casting Ambush [Combat]")
+ return true
+ end
+ end
+ end
end
- -- Racials
- -- arcane_torrent,if=energy.deficit>=15+energy.regen
- -- arcane_pulse
- -- lights_judgment
- if ui.useCDs() and ui.checked("Racial") and cast.able.racial() and not var.stealthAll then
- if unit.race() == "LightforgedDraenei" then
- if cast.racial("target") then
- ui.debug("Casting Racial [Combat]")
+ -- Pool Resource for Shadow Dance (only if available)
+ -- pool_resource,for_next=1,extra_amount=75
+ -- shadow_dance,if=energy>=75&buff.stealth.down&buff.vanish.down&debuff.find_weakness.down
+ if cast.able.shadowDance() and ui.alwaysCdAoENever("Shadow Dance", 3, #enemies.yards10) then
+ if energy() >= 75 and not buff.stealth.exists() and not buff.vanish.exists() and not debuff.findWeakness.exists(units.dyn5) then
+ if cast.shadowDance() then
+ ui.debug("Casting Shadow Dance [Combat]")
return true
end
+ elseif not buff.stealth.exists() and not buff.vanish.exists() and not debuff.findWeakness.exists(units.dyn5) and energy() < 75 then
+ -- Pool 75 energy
+ return true
end
- if (unit.race() == "BloodElf" and energy.deficit() >= 15 + energy.regen()) or unit.race() == "Nightborne" then
- if cast.racial("player") then
- ui.debug("Casting Racial [Combat]")
+ end
+ -- Pool Resource for Vanish (only if available)
+ -- pool_resource,for_next=1,extra_amount=45
+ -- vanish,if=energy>=45&energy<=75&combo_points<=3&buff.shadow_dance.down&buff.master_of_subtlety.down&debuff.find_weakness.down
+ if cast.able.vanish() and ui.alwaysCdAoENever("Vanish", 3, #enemies.yards10) then
+ if energy() >= 45 and energy() <= 75 and comboPoints() <= 3
+ and not buff.shadowDance.exists() and not buff.masterOfSubtlety.exists()
+ and not debuff.findWeakness.exists(units.dyn5)
+ then
+ if cast.vanish() then
+ ui.debug("Casting Vanish [Combat]")
return true
end
+ elseif energy() < 45 and comboPoints() <= 3
+ and not buff.shadowDance.exists() and not buff.masterOfSubtlety.exists()
+ and not debuff.findWeakness.exists(units.dyn5)
+ then
+ -- Pool 45 energy
+ return true
end
end
- -- Shuriken Toss
- if ui.checked("Shuriken Toss OOR") and energy() >= ui.value("Shuriken Toss OOR") and cast.able.shurikenToss()
- and (charges.shadowstep.count() == 0 or not ui.checked("Shadowstep")) and unit.distance(units.dyn30) > 5 and not var.stealthAll
- then
- if cast.shurikenToss() then
- ui.debug("Casting Shuriken Toss - Out of Melee [Combat]")
+ -- Marked for Death (only if talented)
+ -- marked_for_death,if=talent.marked_for_death.enabled&combo_points=0
+ if talent.markedForDeath and ui.checked("Marked for Death") and cast.able.markedForDeath(units.dyn5) and comboPoints() == 0 then
+ if cast.markedForDeath(units.dyn5) then
+ ui.debug("Casting Marked for Death [Combat]")
return true
end
end
- end -- End In Combat
-end -- End Action List - Combat
+ -- Generator (for building Slice and Dice / Rupture) - only if Anticipation talented
+ -- run_action_list,name=generator,if=talent.anticipation.enabled&anticipation_charges<4&buff.slice_and_dice.up&dot.rupture.remains>2&(buff.slice_and_dice.remains<6|dot.rupture.remains<4)
+ if talent.anticipation and buff.anticipation.stack() < 4 and buff.sliceAndDice.exists() and debuff.rupture.remains(units.dyn5) > 2
+ and (buff.sliceAndDice.remains() < 6 or debuff.rupture.remains(units.dyn5) < 4)
+ then
+ if actionList.Generator() then return true end
+ end
+ -- Finisher (flexible combo point requirement for leveling)
+ -- run_action_list,name=finisher,if=combo_points>=5 or (combo_points>=3 and level<20)
+ if comboPoints() >= 5 or (comboPoints() >= 3 and unit.level("player") < 20) then
+ if actionList.Finisher() then return true end
+ end
+ -- Generator
+ -- run_action_list,name=generator,if=combo_points<4|energy>80|talent.anticipation.enabled
+ if comboPoints() < 4 or energy() > 80 or talent.anticipation then
+ if actionList.Generator() then return true end
+ end
+ -- Pool
+ -- run_action_list,name=pool
+ if actionList.Pool() then return true end
+ end
+end -- End Action List - Combat
----------------
--- ROTATION ---
@@ -1158,6 +1186,7 @@ local function runRotation()
equiped = br.player.equiped
items = br.player.items
module = br.player.module
+ spell = br.player.spell
talent = br.player.talent
ui = br.player.ui
unit = br.player.unit
@@ -1166,97 +1195,44 @@ local function runRotation()
end
-- Get Best Unit for Range
- -- units.get(range, aoe)
units.get(5)
+ units.get(10, true)
units.get(30)
-- Get List of Enemies for Range
- -- enemies.get(range, from unit, no combat, variable)
enemies.get(5)
enemies.get(10)
- enemies.get(10, "player", true)
enemies.get(20)
- enemies.get(20, "player", true)
enemies.get(30)
-- General Vars
- var.stealth = buff.stealth.exists() or buff.vanish.exists() or buff.shadowDance.exists()
- var.stealthAll = buff.stealth.exists() or buff.vanish.exists() or buff.shadowmeld.exists() or
- buff.shadowDance.exists() or buff.stealthSepsis.exists() or buff.subterfuge.exists()
-
- -- Numeric Returns
- var.gloomShadowVeins = (talent.gloomblade and buff.lingeringShadow.remains() >= 6 or buff.perforatedVeins.exists()) and
- 1 or 0
- var.deepSecrets = (talent.deeperStratagem or talent.secretStratagem) and 1 or 0
- var.deepStrat = talent.deeperStratagem and 1 or 0
- var.secretStrat = talent.secretStratagem and 1 or 0
- var.shadowcraft = talent.shadowcraft and 1 or 0
- var.shadowDance = talent.shadowDance and 1 or 0
- var.subterfuge = talent.subterfuge and 3 or 0
- var.thistleTea = talent.thistleTea and 1 or 0
- var.vigor = talent.vigor and 1 or 0
- var.invigoratingTier = (not talent.invigoratingShadowdust or equiped.tier(30, 2)) and 1 or 0
-
- -- SimC Specific Variables
- -- variable,name=secret_condition,value=(action.gloomblade.used_for_danse|action.shadowstrike.used_for_danse|action.backstab.used_for_danse|action.shuriken_storm.used_for_danse)&(action.eviscerate.used_for_danse|action.black_powder.used_for_danse|action.rupture.used_for_danse)|!talent.danse_macabre
- var.secretCondition = ((var.danseGloomblade or var.danseShadowstrike or var.danseBackstab or var.danseShurikenStorm) and (var.danseEviscerate or var.danseBlackPowder or var.danseRupture) or not talent.danseMacabre)
- -- cp_max_spend
- var.cpMaxSpend = 5 + var.deepStrat + var.secretStrat
- -- effective_combo_points
- var.animaCharged = buff.echoingReprimand.exists() and 2 or 0
- var.effectiveComboPoints = (var.animaCharged > 0 and comboPoints() == var.animaCharged) and 7 or comboPoints()
- if talent.danseMacabre and buff.shadowDance.exists() then
- -- action.backstab.used_for_danse
- if cast.last.backstab() then
- var.danseBackstab = true
- end
- -- action.black_powder.used_for_danse
- if cast.last.blackPowder() then
- var.danseBlackPowder = true
- end
- -- action.eviscerate.used_for_danse
- if cast.last.eviscerate() then
- var.danseEviscerate = true
- end
- -- action.gloomblade.used_for_danse
- if cast.last.gloomblade() then
- var.danseGloomblade = true
- end
- -- action.rupture.used_for_danse
- if cast.last.rupture() then
- var.danseRupture = true
- end
- -- action.shadowstrike.used_for_danse
- if cast.last.shadowstrike() then
- var.danseShadowstrike = true
- end
- -- action.shuriken_storm.used_for_danse
- if cast.last.shurikenStorm() then
- var.danseShurikenStorm = true
- end
- else
- var.danseBackstab = false
- var.danseBlackPowder = false
- var.danseEviscerate = false
- var.danseGloomblade = false
- var.danseRupture = false
- var.danseShadowstrike = false
- var.danseShurikenStorm = false
- end
+ var.getTime = br._G.GetTime()
+ var.stealthed = isStealthed()
- if not (not unit.exists("target") or not unit.isUnit("target", var.pickPocketUnit)) then
- br.unpickable = false
- var.pickPocketUnit = "player"
- end
+ -- Reset opener flag when out of combat
+ if not unit.inCombat() then
+ if var.profileStop then var.profileStop = false end
+ var.openerCast = false -- Reset opener flag
+ var.openerTime = nil -- Reset opener time
+ var.openerAttemptTime = nil -- Reset opener attempt timer
+
+ -- Clear Pick Pocket in-flight tracking when out of combat
+ -- BUT only if it's been more than 2 seconds (don't clear if we just cast it)
+ if var.pickPocketCastTime > 0 and (var.getTime - var.pickPocketCastTime) > 2.0 then
+ var.pickPocketCastTime = 0
+ var.pickPocketLastGUID = nil
+ end
- var.maxTTD = 0
- var.maxTTDUnit = "target"
- for i = 1, #enemies.yards5 do
- local thisUnit = enemies.yards5[i]
- local thisCondition = unit.ttd(thisUnit)
- if thisCondition > var.maxTTD then
- var.maxTTD = thisCondition
- var.maxTTDUnit = thisUnit
+ -- Clean up old Pick Pocket tracking (keep recent attempts for persistence)
+ -- Only clear entries that are more than 5 minutes old
+ local currentTime = var.getTime
+ for guid, _ in pairs(var.pickPocketSuccess) do
+ -- Keep successful picks indefinitely during session, only clear on reload
+ end
+ for guid, timestamp in pairs(var.pickPocketAttempted) do
+ if type(timestamp) == "number" and (currentTime - timestamp) > 300 then
+ var.pickPocketAttempted[guid] = nil
+ end
end
end
@@ -1285,11 +1261,13 @@ local function runRotation()
--- In Combat Rotation ---
--------------------------
if actionList.Combat() then return true end
- end -- End Profile
-end -- runRotation
+ end
+end -- runRotation
+
local id = 261
-if br.rotations[id] == nil then br.rotations[id] = {} end
-br._G.tinsert(br.rotations[id], {
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Shaman/Enhancement/BlazinsEnhance.lua b/Rotations/Shaman/Enhancement/BlazinsEnhance.lua
deleted file mode 100644
index 74ac2b7b82..0000000000
--- a/Rotations/Shaman/Enhancement/BlazinsEnhance.lua
+++ /dev/null
@@ -1,994 +0,0 @@
--------------------------------------------------------
--- Author = CuteOne rework by Blazin
--- Patch = 11.0.2
--- Patch should be the latest patch you've updated the rotation for (i.e., 9.2.5)
--- Coverage = 100%
--- Coverage should be your estimated percent coverage for class mechanics (i.e., 100%)
--- Status = Limited
--- Status should be one of: Full, Limited, Sporadic, Inactive, Unknown
--- Readiness = NoRaid
--- Readiness should be one of: Raid, NoRaid, Basic, Development, Untested
--------------------------------------------------------
--- Required: Fill above fields to populate README.md --
--------------------------------------------------------
-
-local rotationName = "Blazin"
----------------
---- Toggles ---
----------------
-local function createToggles()
- -- Rotation Button
- local RotationModes = {
- [1] = { mode = "Auto", value = 1 , overlay = "Automatic Rotation", tip = "Swaps between Single and Multiple based on number of targets in range.", highlight = 1, icon = br.player.spells.windstrike },
- [2] = { mode = "Mult", value = 2 , overlay = "Multiple Target Rotation", tip = "Multiple target rotation used.", highlight = 0, icon = br.player.spells.crashLightning },
- [3] = { mode = "Sing", value = 3 , overlay = "Single Target Rotation", tip = "Single target rotation used.", highlight = 0, icon = br.player.spells.stormstrike },
- [4] = { mode = "Off", value = 4 , overlay = "DPS Rotation Disabled", tip = "Disable DPS Rotation", highlight = 0, icon = br.player.spells.healingSurge }
- };
- br.ui:createToggle(RotationModes, "Rotation", 1, 0)
- -- Cooldown Button
- local CooldownModes = {
- [1] = { mode = "Auto", value = 1 , overlay = "Cooldowns Automated", tip = "Automatic Cooldowns - Boss Detection.", highlight = 1, icon = br.player.spells.feralSpirit },
- [2] = { mode = "On", value = 1 , overlay = "Cooldowns Enabled", tip = "Cooldowns used regardless of target.", highlight = 0, icon = br.player.spells.feralSpirit },
- [3] = { mode = "Off", value = 3 , overlay = "Cooldowns Disabled", tip = "No Cooldowns will be used.", highlight = 0, icon = br.player.spells.feralSpirit }
- };
- br.ui:createToggle(CooldownModes,"Cooldown",2,0)
- -- Defensive Button
- local DefensiveModes = {
- [1] = { mode = "On", value = 1 , overlay = "Defensive Enabled", tip = "Includes Defensive Cooldowns.", highlight = 1, icon = br.player.spells.astralShift },
- [2] = { mode = "Off", value = 2 , overlay = "Defensive Disabled", tip = "No Defensives will be used.", highlight = 0, icon = br.player.spells.astralShift }
- };
- br.ui:createToggle(DefensiveModes,"Defensive",3,0)
- -- Interrupt Button
- local InterruptModes = {
- [1] = { mode = "On", value = 1 , overlay = "Interrupts Enabled", tip = "Includes Basic Interrupts.", highlight = 1, icon = br.player.spells.windShear },
- [2] = { mode = "Off", value = 2 , overlay = "Interrupts Disabled", tip = "No Interrupts will be used.", highlight = 0, icon = br.player.spells.windShear }
- };
- br.ui:createToggle(InterruptModes,"Interrupt",4,0)
-end
-
----------------
---- OPTIONS ---
----------------
-local function createOptions()
- local optionTable
-
- local function rotationOptions()
- local section
- local alwaysCdNever = {"|cff00FF00Always","|cffFFFF00Cooldowns","|cffFF0000Never"}
- -- General Options
- section = br.ui:createSection(br.ui.window.profile, "General")
- -- Dummy DPS Test
- br.ui:createSpinner(section, "DPS Testing", 5, 5, 60, 5, "|cffFFFFFFSet to desired time for test in minuts. Min: 5 / Max: 60 / Interval: 5")
- -- Pre-Pull Timer
- br.ui:createSpinner(section, "Pre-Pull Timer", 5, 1, 10, 1, "|cffFFFFFFSet to desired time to start Pre-Pull (DBM Required). Min: 1 / Max: 10 / Interval: 1")
- -- Ghost Wolf
- br.ui:createDropdownWithout(section,"Ghost Wolf", {"|cff00FF00Always","|cffFFFF00OoC Only","|cffFF0000Never"}, 1, "|cffFFFFFFWhen to use Ghost Wolf.")
- -- Feral Lunge
- br.ui:createCheckbox(section,"Feral Lunge")
- -- Lightning Bolt OOC
- br.ui:createCheckbox(section,"Lightning Bolt Out of Combat")
- -- Lightning Filler
- br.ui:createCheckbox(section,"Lightning Filler","Select to use LB/CL when all other abilities are on CD and can cast before any available.")
- -- Shields Up
- br.ui:createDropdownWithout(section, "Shields Up", {"Lightning Shield","Earth Shield","No Shields"}, 1, "|cffFFFFFFSet to desired shield to use.")
- -- Spirit Walk
- br.ui:createCheckbox(section,"Spirit Walk")
- -- Water Walking
- br.ui:createCheckbox(section,"Water Walking")
- -- Weapon Imbues
- br.ui:createCheckbox(section,"Windfury Weapon")
- br.ui:createCheckbox(section,"Flametongue Weapon")
- -- Skyfury
- br.ui:createCheckbox(section,"Skyfury")
- br.ui:checkSectionState(section)
- -- Cooldown Options
- section = br.ui:createSection(br.ui.window.profile, "Cooldowns")
- -- Agi Pot
- br.ui:createCheckbox(section,"Potion")
- -- Flask Up Module
- br.player.module.FlaskUp("Agility",section)
- -- Racial
- br.ui:createDropdownWithout(section,"Racial", alwaysCdNever, 1, "|cffFFFFFFWhen to use Racial.")
- -- Basic Trinkets Module
- br.player.module.BasicTrinkets(nil,section)
- -- Ascendance
- br.ui:createDropdownWithout(section,"Ascendance", alwaysCdNever, 1, "|cffFFFFFFWhen to use Ascendance.")
- -- Earth Elemental Totem
- br.ui:createDropdownWithout(section,"Earth Elemental", alwaysCdNever, 1, "|cffFFFFFFWhen to use Earth Elemental.")
- -- Feral Spirit
- br.ui:createDropdownWithout(section,"Feral Spirit", alwaysCdNever, 1, "|cffFFFFFFWhen to use Feral Spirit.")
- -- Primordial Wave
- br.ui:createDropdownWithout(section,"Primordial Wave", alwaysCdNever, 1, "|cffFFFFFFWhen to use Primordial Wave.")
- -- Sundering
- br.ui:createDropdownWithout(section,"Sundering", alwaysCdNever, 1, "|cffFFFFFFWhen to use Sundering.")
- br.ui:checkSectionState(section)
- -- Defensive Options
- section = br.ui:createSection(br.ui.window.profile, "Defensive")
- -- Basic Healing Module
- br.player.module.BasicHealing(section)
- -- Ancestral Spirit
- br.ui:createDropdown(section, "Ancestral Spirit", {"|cffFFFF00Selected Target","|cffFF0000Mouseover Target"}, 1, "|ccfFFFFFFTarget to Cast On")
- -- Astral Shift
- br.ui:createSpinner(section, "Astral Shift", 40, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
- -- Cleanse Spirit
- br.ui:createDropdown(section, "Cleanse Spirit", {"|cff00FF00Player Only","|cffFFFF00Selected Target","|cffFF0000Mouseover Target"}, 1, "|ccfFFFFFFTarget to Cast On")
- -- Healing Surge
- br.ui:createSpinner(section, "Healing Surge", 50, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
- br.ui:createSpinnerWithout(section, "Healing Surge OoC", 90, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
- -- Auto/Insta-Heal
- br.ui:createDropdownWithout(section, "Heal Target", { "|cffFFDD11LowestHP", "|cffFFDD11Player"}, 1, "|cffFFFFFFSelect Target to Heal")
- br.ui:createDropdownWithout(section, "Instant Behavior", {"|cff00FF00Always","|cffFFFF00Combat Only","|cffFF0000Never"}, 2, "|cffFFFFFFSelect how to use Instant Heal proc.")
- -- Healing Steam Totem
- br.ui:createSpinner(section, "Healing Stream Totem", 35, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
- br.ui:createCheckbox(section, "Use HST While Solo")
- br.ui:createSpinnerWithout(section, "Healing Stream Totem - Min Units", 1, 0, 5, 1, "|cffFFFFFFNumber of Units below HP Level to Cast At")
- -- Lightning Surge Totem
- br.ui:createSpinner(section, "Capacitor Totem - HP", 30, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
- br.ui:createSpinner(section, "Capacitor Totem - AoE", 5, 0, 10, 1, "|cffFFFFFFNumber of Units in 5 Yards to Cast At")
- -- Purge
- br.ui:createCheckbox(section,"Purge")
- br.ui:checkSectionState(section)
- -- Interrupt Options
- section = br.ui:createSection(br.ui.window.profile, "Interrupts")
- -- Wind Shear
- br.ui:createCheckbox(section,"Wind Shear")
- -- Hex
- br.ui:createCheckbox(section,"Hex")
- -- Lightning Surge Totem
- br.ui:createCheckbox(section,"Capacitor Totem")
- -- Interrupt Percentage
- br.ui:createSpinnerWithout(section, "Interrupt At", 0, 0, 95, 5, "|cffFFFFFFCast Percent to Cast At")
- br.ui:checkSectionState(section)
- -- Toggle Key Options
- section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
- -- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 4)
- -- Cooldown Key Toggle
- br.ui:createDropdownWithout(section, "Cooldown Mode", br.dropOptions.Toggle, 3)
- -- Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
- -- Interrupts Key Toggle
- br.ui:createDropdownWithout(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
- -- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
- br.ui:checkSectionState(section)
- end
- optionTable = {{
- [1] = "Rotation Options",
- [2] = rotationOptions,
- }}
- return optionTable
-end
-
---------------
---- Locals ---
---------------
--- BR API Locals
-local buff
-local cast
-local cd
-local charges
-local debuff
-local enemies
-local equiped
-local module
-local runeforge
-local talent
-local ui
-local unit
-local units
-local var
-
--- General API Locals
-local actionList = {}
-
-local function canLightning(aoe)
- local level = unit.level()
- if not ui.checked("Lightning Filler") or level < 10 or unit.moving() then return false end
- local timeTillLightning = (aoe and talent.chainLightning) and cast.time.chainLightning() or cast.time.lightningBolt()
-
- local crashLightning = talent.crashLightning and cd.crashLightning.remain() or 99
- local earthenSpike = talent.earthenSpike and cd.earthenSpike.remain() or 99
- local elementalBlast = talent.elementalBlast and cd.elementalBlast.remain() or 99
- local fireNova = talent.fireNova and cd.fireNova.remain() or 99
- local flameShock = cd.flameShock.exists() and cd.flameShock.remain() or debuff.flameShock.exists() and debuff.flameShock.remain() or 99
- local frostShock = talent.frostShock and cd.frostShock.remain() or 99
- local iceStrike = talent.iceStrike and cd.iceStrike.remain() or 99
- local lavaBurst = (talent.lavaBurst and not talent.elementalBlast) and cd.lavaBurst.remain() or 99
- local lavaLash = talent.lavaLash and cd.lavaLash.remain() or 99
- local stormstrike = (talent.stormstrike and not buff.ascendance.exists()) and cd.stormstrike.remain() or 99
- local sundering = talent.sundering and cd.sundering.remain() or 99
- local windstrike = (talent.stormstrike and buff.ascendance.exists()) and cd.stormstrike.remain() or 99
-
- return math.min(crashLightning,earthenSpike,elementalBlast,fireNova,flameShock,frostShock,iceStrike,lavaBurst,lavaLash,stormstrike,sundering,windstrike) > timeTillLightning
-end
-
---------------------
---- Action Lists ---
---------------------
--- Action List - Extras
-actionList.Extras = function()
- local startTime = br._G.debugprofilestop()
- -- Dummy Test
- if ui.checked("DPS Testing") then
- if unit.exists("target") then
- if unit.combatTime() >= (tonumber(ui.value("DPS Testing"))*60) and unit.isDummy() then
- unit.stopAttack()
- unit.clearTarget()
- ui.print(tonumber(ui.value("DPS Testing")) .." Minute Dummy Test Concluded - Profile Stopped")
- var.profileStop = true
- end
- end
- end -- End Dummy Test
- -- Ghost Wolf
- if (ui.value("Ghost Wolf") == 1 or (ui.value("Ghost Wolf") == 2 and not unit.inCombat())) and cast.able.ghostWolf() and not (unit.mounted() or unit.flying()) then
- if ((#enemies.yards20 == 0 and not unit.inCombat()) or (#enemies.yards10 == 0 and unit.inCombat())) and unit.moving("player") and not buff.ghostWolf.exists() then
- if cast.ghostWolf() then ui.debug("Casting Ghost Wolf") return true end
- end
- end
- -- Purge
- if ui.checked("Purge") and cast.able.purge() and cast.dispel.purge("target") and not unit.isBoss() and unit.exists("target") then
- if cast.purge() then ui.debug("Casting Purge") return true end
- end
- -- Spirit Walk
- if ui.checked("Spirit Walk") and cast.able.spiritWalk() and unit.moving() then
- if cast.spiritWalk() then ui.debug("Casting Spirit Walk") return true end
- end
- -- Water Walking
- if ui.checked("Water Walking") and cast.able.waterWalking() and not unit.inCombat() and not buff.waterWalking.exists() then
- if cast.waterWalking() then ui.debug("Casting Water Walking") return true end
- end
- br.debug.cpu:updateDebug(startTime,"rotation.profile.extras")
-end -- End Action List - Extras
--- Action List - Defensive
-actionList.Defensive = function()
- local startTime = br._G.debugprofilestop()
- if ui.useDefensive() then
- -- Basic Healing Module
- module.BasicHealing()
- -- Ancestral Spirit
- if ui.checked("Ancestral Spirit") and cast.timeSinceLast.ancestralSpirit() > 5 then
- if ui.value("Ancestral Spirit")==1 and cast.able.ancestralSpirit("target","dead") and unit.player("target") then
- if cast.ancestralSpirit("target","dead") then ui.debug("Casting Ancestral Spirit [Target]") return true end
- end
- if ui.value("Ancestral Spirit")==2 and cast.able.ancestralSpirit("mouseover","dead") and unit.player("mouseover") then
- if cast.ancestralSpirit("mouseover","dead") then ui.debug("Casting Ancestral Spirit [Mouseover]") return true end
- end
- end
- -- Astral Shift
- if ui.checked("Astral Shift") and cast.able.astralShift() and unit.hp() <= ui.value("Astral Shift") and unit.inCombat() then
- if cast.astralShift() then ui.debug("Casting Astral Shift") return true end
- end
- -- Capacitor Totem
- if ui.checked("Capacitor Totem - HP") and cast.able.capacitorTotem("player","ground") and cd.capacitorTotem.ready()
- and unit.hp() <= ui.value("Capacitor Totem - HP") and unit.inCombat() and #enemies.yards5 > 0
- then
- if cast.capacitorTotem("player","ground") then ui.debug("Casting Capacitor Totem [Low HP]") return true end
- end
- if ui.checked("Capacitor Totem - AoE") and cast.able.capacitorTotem("best",nil,ui.value("Capacitor Totem - AoE"),8) and cd.capacitorTotem.ready()
- and #enemies.yards5 >= ui.value("Capacitor Totem - AoE") and unit.inCombat()
- then
- if cast.capacitorTotem("best",nil,ui.value("Capacitor Totem - AoE"),8) then ui.debug("Casting Capacitor Totem [AOE]") return true end
- end
- -- Cleanse Spirit
- if ui.checked("Cleanse Spirit") then
- if ui.value("Cleanse Spirit")==1 and cast.able.cleanseSpirit("player") and cast.dispel.cleanseSpirit("player") then
- if cast.cleanseSpirit("player") then ui.debug("Casting Cleanse Spirit [Player]") return true end
- end
- if ui.value("Cleanse Spirit")==2 and cast.able.cleanseSpirit("target") and cast.dispel.cleanseSpirit("target") then
- if cast.cleanseSpirit("target") then ui.debug("Casting Cleanse Spirit [Target]") return true end
- end
- if ui.value("Cleanse Spirit")==3 and cast.able.cleanseSpirit("mouseover") and cast.dispel.cleanseSpirit("mouseover") then
- if cast.cleanseSpirit("mouseover") then ui.debug("Casting Cleanse Spirit [Mouseover]") return true end
- end
- end
- -- Earth Shield
- if talent.earthShield and ui.value("Shields Up") == 2 and cast.able.earthShield() and not buff.earthShield.exists() then
- if cast.earthShield("player") then ui.debug("Casting Earth Shield") return true end
- end
- -- Healing Surge
- if ui.checked("Healing Surge") and cast.able.healingSurge(var.healUnit) and not (unit.mounted() or unit.flying())
- and (ui.value("Heal Target") ~= 1 or (ui.value("Heal Target") == 1
- and unit.distance(br.friend[1].unit) < 40)) and not cast.current.healingSurge()
- then
- if not unit.inCombat() then
- -- Lowest Party/Raid or Player
- if (var.healHP <= ui.value("Healing Surge OoC") and not unit.moving())
- and (buff.maelstromWeapon.stack() == 0 or ui.value("Instant Behavior") == 1)
- then
- if cast.healingSurge(var.healUnit) then ui.debug("Casting Healing Surge [OoC] on "..unit.name(var.healUnit)) return true end
- end
- elseif unit.inCombat() and (not unit.moving() or buff.maelstromWeapon.stack() >= 5) then
- -- Lowest Party/Raid or Player
- if var.healHP <= ui.value("Healing Surge") then
- if ui.value("Instant Behavior") == 1 or (ui.value("Instant Behavior") == 2 and buff.maelstromWeapon.stack() >= 5) or (ui.value("Instant Behavior") == 3 and buff.maelstromWeapon.stack() == 0) then
- if buff.maelstromWeapon.stack() >= 5 then
- if cast.healingSurge(var.healUnit) then ui.debug("Casting Healing Surge [IC Instant] on "..unit.name(var.healUnit)) return true end
- else
- if cast.healingSurge(var.healUnit) then ui.debug("Casting Healing Surge [IC Long] on "..unit.name(var.healUnit)) return true end
- end
- end
- end
- end
- end
- -- Healing Stream Totem
- if ui.checked("Healing Stream Totem") and cast.able.healingStreamTotem("player","ground")
- and var.unitsNeedingHealing >= ui.value("Healing Stream Totem - Min Units")
- then
- if cast.healingStreamTotem("player","ground") then ui.debug("Casting Healing Stream Totem") return true end
- end
- end -- End Defensive Toggle
- br.debug.cpu:updateDebug(startTime,"rotation.profile.defensive")
-end -- End Action List - Defensive
--- Action List - Interrupts
-actionList.Interrupts = function()
- -- Wind Shear
- if ui.useInterrupt() and ui.checked("Wind Shear") and cast.able.windShear() then
- local thisUnit
- for i = 1, #enemies.yards30 do
- thisUnit = enemies.yards30[i]
- if unit.interruptable(thisUnit, ui.value("Interrupt At")) then
- if cast.windShear(thisUnit) then
- ui.debug("Casting Wind Shear on " .. unit.name(thisUnit))
- return true
- end
- end
- end
- end
-end -- End Action List - Interrupts
--- Action List - AOE
-actionList.AOE = function()
- local startTime = br._G.debugprofilestop()
- -- Tempest
- -- Tempest
- if cast.able.tempest() and buff.maelstromWeapon.stack() >= 6 then
- if cast.iceStrike() then ui.debug("Casting Tempest") return true end
- end
- -- Crash Lightning
- -- crash_lightning,if=buff.doom_winds.up|!buff.crash_lightning.up
- if #enemies.yards8c > 0 and cast.able.crashLightning("player","cone",1,8) and (buff.doomWinds.exists() or not buff.crashLightning.exists()) then
- if cast.crashLightning("player","cone",1,8) then ui.debug("Casting Crash Lightning [AOE Doom Winds]") return true end
- end
- -- Lightning Bolt
- -- lightning_bolt,if=(active_dot.flame_shock=active_enemies|active_dot.flame_shock=6)&buff.primordial_wave.up&buff.maelstrom_weapon.stack>=(5+5*talent.overflowing_maelstrom.enabled)&(!buff.splintered_elements.up|fight_remains<=12|raid_event.adds.remains<=gcd)
- if cast.able.lightningBolt() and (debuff.flameShock.count() == #enemies.yards40f or debuff.flameShock.count() == 6) and buff.primordialWave.exists()
- and buff.maelstromWeapon.stack() >= (5 + 5 * var.overflowingMaelstrom) and (not buff.splinteredElements.exists())
- then
- if cast.lightningBolt() then ui.debug("Casting Lightning Bolt") return true end
- end
- -- Sundering
- -- sundering,if=buff.doom_winds.up
- if ui.alwaysCdNever("Sundering") and #enemies.yards11r > 0 and cast.able.sundering("player","rect") and buff.doomWinds.exists() then
- if cast.sundering("player","rect",1,11) then ui.debug("Casting Sundering [AOE - Doom Winds]") return true end
- end
- -- Fire Nova
- -- fire_nova,if=active_dot.flame_shock=6|(active_dot.flame_shock>=4&active_dot.flame_shock=active_enemies)
- if #enemies.yards8 > 0 and cast.able.fireNova("player","aoe",1,8) and (debuff.flameShock.count() == 6 or (debuff.flameShock.count() >= 4 and debuff.flameShock.count() == #enemies.yards40)) then
- if cast.fireNova("player","aoe",1,8) then ui.debug("Casting Fire Nova [AOE All/6+ Flame Shock]") return true end
- end
- -- Primodial Wave
- -- primordial_wave,target_if=min:dot.flame_shock.remains,cycle_targets=1,if=!buff.primordial_wave.up
- if ui.alwaysCdNever("Primordial Wave") and cast.able.primordialWave(var.lowestFlameShock) and not buff.primordialWave.exists() then
- if cast.primordialWave(var.lowestFlameShock) then ui.debug("Casting Primordial Wave [AOE]") return true end
- end
- -- Windstrike
- -- windstrike,if=talent.thorims_invocation.enabled&ti_chain_lightning&buff.maelstrom_weapon.stack>1
- if cast.able.windstrike() and buff.ascendance.exists() and talent.thorimsInvocation and buff.maelstromWeapon.stack() > 1 then
- if cast.windstrike() then ui.debug("Casting Windstrike [AOE]") return true end
- end
- -- Lava Lash
- -- lava_lash,target_if=min:debuff.lashing_flames.remains,cycle_targets=1,if=talent.lashing_flames.enabled&dot.flame_shock.ticking&(active_dot.flame_shock 0 and cast.able.sundering("player","rect") then
- if cast.sundering("player","rect",1,11) then ui.debug("Casting Sundering [AOE]") return true end
- end
- -- Fire Nova
- -- fire_nova,if=active_dot.flame_shock>=4
- if #enemies.yards8 > 0 and cast.able.fireNova("player","aoe",1,8) and debuff.flameShock.count() >= 4 then
- if cast.fireNova("player","aoe",1,8) then ui.debug("Casting Fire Nova [AOE 4+ Flame Shock]") return true end
- end
- -- Lava Lash
- -- lava_lash,target_if=min:debuff.lashing_flames.remains,cycle_targets=1,if=talent.lashing_flames.enabled
- if cast.able.lavaLash(var.lowestLashingFlames) and talent.lashingFlames then
- if cast.lavaLash(var.lowestLashingFlames) then ui.debug("Casting Lava Lash [AOE") return true end
- end
- -- Fire Nova
- -- fire_nova,if=active_dot.flame_shock>=3
- if #enemies.yards8 > 0 and cast.able.fireNova("player","aoe",1,8) and debuff.flameShock.count() >= 3 then
- if cast.fireNova("player","aoe",1,8) then ui.debug("Casting Fire Nova [AOE 3+ Flame Shock]") return true end
- end
- -- Elemental Blast
- -- elemental_blast,if=(!talent.elemental_spirits.enabled|(talent.elemental_spirits.enabled&(charges=max_charges|buff.feral_spirit.up)))&buff.maelstrom_weapon.stack=10&(!talent.crashing_storms.enabled|active_enemies<=3)
- if cast.able.elementalBlast() and (not talent.elementalSpirits or (talent.elementalSpirits
- and (charges.elementalBlast.count == charges.elementalBlast.max() or buff.feralSpirit.exists())))
- and buff.maelstromWeapon.stack() == 10 and (not talent.crashingStorms or #enemies.yards40f <= 3)
- then
- if cast.elementalBlast() then ui.debug("Casting Elemental Blast [AOE 10 Maelstrom]") return true end
- end
- -- Chain Lightning
- -- chain_lightning,if=buff.maelstrom_weapon.stack=10
- if cast.able.chainLightning() and buff.maelstromWeapon.stack() >= 10 then
- if cast.chainLightning() then ui.debug("Casting Chain Lightning [AOE 10 Maelstrom]") return true end
- end
- -- Crash Lightning
- -- crash_lightning,if=buff.cl_crash_lightning.up
- if #enemies.yards8c > 0 and cast.able.crashLightning("player","cone",1,8) and buff.clCrashLightning.exists() then
- if cast.crashLightning("player","cone",1,8) then ui.debug("Casting Crash Lightning [AOE Buffed Crash Lightning]") return true end
- end
- -- Lava Lash
- -- lava_lash,if=buff.crash_lightning.up&buff.ashen_catalyst.stack=8
- if cast.able.lavaLash() and buff.crashLightning.exists() and buff.ashenCatalyst.stack() == 8 then
- if cast.lavaLash() then ui.debug("Casting Lava Lash [AOE 8 Ashen Catalyst]") return true end
- end
- -- Windstrike
- -- windstrike,if=buff.crash_lightning.up
- if cast.able.windstrike() and buff.ascendance.exists() and buff.crashLightning.exists() then
- if cast.windstrike() then ui.debug("Casting Windstrike [AOE Crash Lightning]") return true end
- end
- -- Stormstrike
- -- stormstrike,if=buff.crash_lightning.up&(buff.converging_storms.stack=6|(set_bonus.tier29_2pc&buff.maelstrom_of_elements.down&buff.maelstrom_weapon.stack<=5))
- if cast.able.stormstrike() and not buff.ascendance.exists() and buff.crashLightning.exists()
- and (equiped.tier(29) >= 2 and not buff.maelstromOfElements.exists() and buff.maelstromWeapon.stack() <= 5) and buff.awakeningStorms.stack() < 2 and not buff.tempest.exists() then
- if cast.stormstrike() then ui.debug("Casting Stormstrike [AOE Crash Lightning Converging Storms]") return true end
- end
- -- Lava Lash
- -- lava_lash,if=buff.crash_lightning.up,if=talent.molten_assault.enabled
- if cast.able.lavaLash() and buff.crashLightning.exists and talent.moltenAssault then
- if cast.lavaLash() then ui.debug("Casting Lava Lash [AOE Crash Lightning Molten Assault]") return true end
- end
- -- Ice Strike
- -- ice_strike,if=buff.crash_lightning.up,if=talent.swirling_maelstrom.enabled
- if cast.able.iceStrike() and buff.crashLightning.exists and talent.moltenAssault then
- if cast.iceStrike() then ui.debug("Casting Ice Strike [AOE Crash Lightning Molten Assault]") return true end
- end
- -- Stormstrike
- -- stormstrike,if=buff.crash_lightning.up
- if cast.able.stormstrike() and not buff.ascendance.exists() and buff.crashLightning.exists() and buff.awakeningStorms.stack() < 2 and not buff.tempest.exists() then
- if cast.stormstrike() then ui.debug("Casting Stormstrike [AOE Crash Lightning]") return true end
- end
- -- Ice Strike
- -- ice_strike,if=buff.crash_lightning.up
- if cast.able.iceStrike() and buff.crashLightning.exists and talent.moltenAssault then
- if cast.iceStrike() then ui.debug("Casting Ice Strike [AOE Crash Lightning]") return true end
- end
- -- Lava Lash
- -- lava_lash,if=buff.crash_lightning.up
- if cast.able.lavaLash() and buff.crashLightning.exists then
- if cast.lavaLash() then ui.debug("Casting Lava Lash [AOE Crash Lightning]") return true end
- end
- -- Elemental Blast
- -- elemental_blast,if=(!talent.elemental_spirits.enabled|(talent.elemental_spirits.enabled&(charges=max_charges|buff.feral_spirit.up)))&buff.maelstrom_weapon.stack>=5&(!talent.crashing_storms.enabled|active_enemies<=3)
- if cast.able.elementalBlast() and (not talent.elementalSpirits or (talent.elementalSpirits
- and (charges.elementalBlast.count() == charges.elementalBlast.max() or buff.feralSpirit.exists()))) and buff.maelstromWeapon.stack() >= 5
- and (not talent.crashingStorms or #enemies.yards40f <= 3)
- then
- if cast.elementalBlast() then ui.debug("Casting Elemental Blast [AOE]") return true end
- end
- -- Fire Nova
- -- fire_nova,if=active_dot.flame_shock>=2
- if #enemies.yards8 > 0 and cast.able.fireNova("player","aoe",1,8) and debuff.flameShock.count() >= 2 then
- if cast.fireNova("player","aoe",1,8) then ui.debug("Casting Fire Nova [AOE 2+ Flame Shock]") return true end
- end
- -- Crash Lightning
- -- crash_lightning
- if #enemies.yards8c > 0 and cast.able.crashLightning("player","cone",1,8) then
- if cast.crashLightning("player","cone",1,8) then ui.debug("Casting Crash Lightning [AOE]") return true end
- end
- -- Windstrike
- -- windstrike
- if cast.able.windstrike() and buff.ascendance.exists() then
- if cast.windstrike() then ui.debug("Casting Windstrike [AOE]") return true end
- end
- -- Lava lash
- -- lava_lash,if=talent.molten_assault.enabled
- if cast.able.lavaLash() and talent.moltenAssault then
- if cast.lavaLash() then ui.debug("Casting Lava Lash [AOE Molten Assault]") return true end
- end
- -- Ice Strike
- -- ice_strike,if=talent.swirling_maelstrom.enabled
- if cast.able.iceStrike() and talent.swirlingMaelstrom then
- if cast.iceStrike() then ui.debug("Casting Ice Strike [AOE Swirling Maelstrom]") return true end
- end
- -- Stormstrike
- -- stormstrike
- if cast.able.stormstrike() and unit.level() >= 20 then
- if cast.stormstrike() then ui.debug("Casting Stormstrike [AOE]") return true end
- end
- -- Ice Strike
- -- ice_strike
- if cast.able.iceStrike() then
- if cast.iceStrike() then ui.debug("Casting Ice Strike [AOE]") return true end
- end
- -- Lava lash
- -- lava_lash
- if cast.able.lavaLash() then
- if cast.lavaLash() then ui.debug("Casting Lava Lash [AOE]") return true end
- end
- -- Flame Shock
- -- flame_shock,target_if=refreshable,cycle_targets=1
- if cast.able.flameShock() then
- for i = 1, #enemies.yards40f do
- local thisUnit = enemies.yards40f[i]
- if debuff.flameShock.refresh(thisUnit) then
- if cast.flameShock(thisUnit) then ui.debug("Casting Flame Shock [AOE]") return true end
- end
- end
- end
- -- Frost Shock
- -- frost_shock
- if cast.able.frostShock() then
- if cast.frostShock() then ui.debug("Casting Frost Shock [AOE]") return true end
- end
- -- Chain Lightning
- -- chain_lightning,if=buff.maelstrom_weapon.stack>=5
- if cast.able.chainLightning() then
- if buff.maelstromWeapon.stack() >= 5 then
- if cast.chainLightning() then ui.debug("Casting Chain Lightning [AOE 5+ Maelstrom]") return true end
- end
- if unit.distance("target") > 5 and not unit.moving() then
- if cast.chainLightning() then ui.debug("Casting Chain Lightning [AOE Out of Range]") return true end
- end
- end
- -- Earth Elemental
- -- earth_elemental
- if ui.alwaysCdNever("Earth Elemental") and cast.able.earthElemental() and ((#enemies.yards8 > 1 and unit.ttd("target") > 15) or unit.isDummy("target")) then
- if cast.earthElemental() then ui.debug("Casting Earth Elemental [AOE]") return true end
- end
- -- Primal Strike
- if cast.able.primalStrike() and unit.level() < 20 then
- if cast.primalStrike() then ui.debug("Casting Primal Strike [AOE]") return true end
- end
- -- Chain Lightning / Lightning Bolt
- if cast.able.chainLightning() and unit.level() >= 24 and canLightning(true) then
- if cast.chainLightning() then var.fillLightning = true ui.debug("Casting Chain Lightning [AOE - Filler]") return true end
- end
- if cast.able.lightningBolt() and unit.level() < 24 and canLightning() then
- if cast.lightningBolt() then var.fillLightning = true ui.debug("Casting Lightning Bolt [AOE - Filler]") return true end
- end
- br.debug.cpu:updateDebug(startTime,"rotation.profile.aoe")
-end -- End Action List - AOE
--- Action List - Single
-actionList.Single = function()
- local startTime = br._G.debugprofilestop()
- -- Tempest
- -- Tempest
- if cast.able.tempest() and buff.maelstromWeapon.stack() >= 6 then
- if cast.iceStrike() then ui.debug("Casting Tempest") return true end
- end
- -- Windstrike
- -- windstrike,if=talent.thorims_invocation.enabled&buff.maelstrom_weapon.stack>=1
- if cast.able.windstrike() and buff.ascendance.exists() and talent.thorimsInvocation and buff.maelstromWeapon.stack() >= 1 then
- if cast.windstrike() then ui.debug("Casting Windstrike") return true end
- end
- -- Lava Last
- -- lava_lash,if=buff.hot_hand.up|buff.ashen_catalyst.stack=8|(buff.ashen_catalyst.stack>=5&buff.maelstrom_of_elements.up&buff.maelstrom_weapon.stack<=6)
- if cast.able.lavaLash() and (buff.hotHand.exists() or (buff.ashenCatalyst.stack() == 8 or (buff.ashenCatalyst.stack() >= 5 and buff.maelstromOfElements.exists() and buff.maelstromWeapon.stack() <= 6))) then
- if cast.lavaLash() then ui.debug("Casting Lava Lash [ST - Hot Hand / Ashen Catalyst]") return true end
- end
- -- Stormstrike
- -- stormstrike,if=buff.doom_winds.up
- if cast.able.stormstrike() and buff.doomWinds.exists() and buff.awakeningStorms.stack() < 2 and not buff.tempest.exists() then
- if cast.stormstrike() then ui.debug("Casting Stormstrike [ST - Doom Winds]") return true end
- end
- -- Ice Strike
- -- ice_strike,if=buff.doom_winds.up
- if cast.able.iceStrike() and buff.doomWinds.exists() then
- if cast.iceStrike() then ui.debug("Casting Ice Strike [ST - Doom Winds]") return true end
- end
- -- Crash Lightning
- -- crash_lightning,if=buff.doom_winds.up
- if #enemies.yards8c > 0 and cast.able.crashLightning("player","cone",1,8) and buff.doomWinds.exists() then
- if cast.crashLightning("player","cone",1,8) then ui.debug("Casting Crash Lightning [ST - Doom Winds]") return true end
- end
-
- -- Sundering
- -- sundering,if=buff.doom_winds.up
- if ui.alwaysCdNever("Sundering") and #enemies.yards11r > 0 and cast.able.sundering("player","rect",1,11) and buff.doomWinds.exists() then
- if cast.sundering("player","rect",1,11) then ui.debug("Casting Sundering [ST - Doom Winds]") return true end
- end
- -- Primordial Wave
- -- primordial_wave,if=buff.primordial_wave.down&(raid_event.adds.in>42|raid_event.adds.in<6)
- if ui.alwaysCdNever("Primordial Wave") and not buff.primordialWave.exists() then
- if cast.primordialWave() then ui.debug("Casting Primordial Wave [ST]") return true end
- end
- -- Flame Shock
- -- flame_shock,if=!ticking
- if cast.able.flameShock() and not debuff.flameShock.exists(units.dyn40) then
- if cast.flameShock() then ui.debug("Casting Flame Shock [ST No Exist]") return true end
- end
- -- Lightning Bolt
- -- lightning_bolt,if=buff.maelstrom_weapon.stack>=5&buff.primordial_wave.up&raid_event.adds.in>buff.primordial_wave.remains&(!buff.splintered_elements.up|fight_remains<=12)
- if cast.able.lightningBolt() and buff.maelstromWeapon.stack() >= 5 and buff.primordialWave.exists() and not buff.splinteredElements.exists() then
- if cast.lightningBolt() then ui.debug("Casting Lighning Bolt [ST 5+ Maelstrom Primordial Wave") return true end
- end
- -- Elemental Blast
- -- elemental_blast,if=talent.elemental_spirits.enabled&(charges=max_charges|buff.feral_spirit.up)&buff.maelstrom_weapon.stack>=8
- if cast.able.elementalBlast() and talent.elementalSpirits and (charges.elementalBlast.count() == charges.elementalBlast.max() or buff.feralSpirit.exists()) and buff.maelstromWeapon.stack() >= 8 then
- if cast.elementalBlast() then ui.debug("Casting Elemental Blast [ST 8+ Maelstrom]") return true end
- end
- -- Ice Strike
- -- ice_strike,if=talent.hailstorm
- if cast.able.iceStrike() and talent.hailstorm then
- if cast.iceStrike() then ui.debug("Casting Ice Strike [ST Hailstorm]") return true end
- end
- -- Stormstrike
- -- stormstrike,if=set_bonus.tier29_2pc&buff.maelstrom_of_elements.down&buff.maelstrom_weapon.stack<=5
- if cast.able.stormstrike() and not buff.ascendance.exists() and equiped.tier(29) >= 2 and not buff.maelstromOfElements.exists() and buff.maelstromWeapon.stack() <= 5 and buff.awakeningStorms.stack() < 2 and not buff.tempest.exists() then
- if cast.stormstrike() then ui.debug("Casting Stormstrike [ST T29 2pc]") return true end
- end
- -- Frost Shock
- -- frost_shock,if=buff.hailstorm.up
- if cast.able.frostShock() and buff.hailstorm.exists() then
- if cast.frostShock() then ui.debug("Casting Frost Shock [ST Hailstorm]") return true end
- end
- -- Lava Lash
- -- lava_lash,if=talent.molten_assault.enabled&dot.flame_shock.refreshable
- if cast.able.lavaLash() and talent.moltenAssault and debuff.flameShock.refresh(units.dyn5) then
- if cast.lavaLash() then ui.debug("Casting Lava Lash [ST Molten Assault]") return true end
- end
- -- Windstrike
- -- windstrike,if=talent.deeply_rooted_elements.enabled|buff.earthen_weapon.up|buff.legacy_of_the_frost_witch.up
- if cast.able.windstrike() and buff.ascendance.exists() and (talent.deeplyRootedElements or buff.earthenWeapon.exists() or buff.legacyOfTheFrostWitch.exists()) then
- if cast.windstrike() then ui.debug("Casting Windstrike [ST Deeply Rooted Eleemnts / Earthen Weapon / Frost Witch]") return true end
- end
- -- Stormstrike
- -- stormstrike,if=talent.deeply_rooted_elements.enabled|buff.earthen_weapon.up|buff.legacy_of_the_frost_witch.up
- if cast.able.stormstrike() and not buff.ascendance.exists() and buff.awakeningStorms.stack() < 2 and (talent.deeplyRootedElements or buff.earthenWeapon.exists() or buff.legacyOfTheFrostWitch.exists()) then
- if cast.stormstrike() then ui.debug("Casting Stormstrike [ST Deeply Rooted Eleemnts / Earthen Weapon / Frost Witch]") return true end
- end
- -- Elemental Blast
- -- elemental_blast,if=(talent.elemental_spirits.enabled&buff.maelstrom_weapon.stack=10)|(!talent.elemental_spirits.enabled&buff.maelstrom_weapon.stack>=5)
- if cast.able.elementalBlast() and ((talent.elementalSpirits and buff.maelstromWeapon.stack() == 10) or (not talent.elementalSpirits and buff.maelstromWeapon.stack() >= 5)) then
- if cast.elementalBlast() then ui.debug("Casting Elemental Blast [ST 10 Maelstrom / No Elemental Spirits 5+ Maelstrom]") return true end
- end
- -- Lava Burst
- -- lava_burst,if=buff.maelstrom_weapon.stack>=5
- if cast.able.lavaBurst() and buff.maelstromWeapon.stack() >= 5 then
- if cast.lavaBurst() then ui.debug("Casting Lava Burst [ST 5+ Maestrom]") return true end
- end
- -- Lightning Bolt
- -- lightning_bolt,if=buff.maelstrom_weapon.stack=10&buff.primordial_wave.down
- if cast.able.lightningBolt() and buff.maelstromWeapon.stack() >= 5 and not buff.primordialWave.exists() then
- if cast.lightningBolt() then ui.debug("Casting Lightning Bolt [ST 10 Maelstrom]") return true end
- end
- -- Windstrike
- -- windstrike
- if cast.able.windstrike() and buff.ascendance.exists() then
- if cast.windstrike() then ui.debug("Casting Windstrike [ST]") return true end
- end
- -- Stormstrike
- -- stormstrike
- if cast.able.stormstrike() and unit.level() >= 20 then
- if cast.stormstrike() then ui.debug("Casting Stormstrike [ST]") return true end
- end
- -- Ice Strike
- -- ice_strike
- if cast.able.iceStrike() then
- if cast.iceStrike() then ui.debug("Casting Ice Strike [ST]") return true end
- end
- -- Lava Lash
- -- lava_lash
- if cast.able.lavaLash() then
- if cast.lavaLash() then ui.debug("Casting Lava Lash [ST]") return true end
- end
- -- Elemental Blast
- -- elemental_blast,if=talent.elemental_spirits.enabled&(charges=max_charges|buff.feral_spirit.up)&buff.maelstrom_weapon.stack>=5
- if cast.able.elementalBlast() and talent.elementalSpirits and (charges.elementalBlast.count() == charges.elementalBlast.max() or buff.feralSpirit.exists()) and buff.maelstromWeapon.stack() >= 5 then
- if cast.elementalBlast() then ui.debug("Casting Elemental Blast [ST 5+ Maelstrom]") return true end
- end
- -- Racial - Bag of Tricks
- -- bag_of_tricks
-
- -- Lightning Bolt
- -- lightning_bolt,if=buff.maelstrom_weapon.stack>=5&buff.primordial_wave.down
- if cast.able.lightningBolt() then
- if buff.maelstromWeapon.stack() >= 5 and not buff.primordialWave.exists() then
- if cast.lightningBolt() then ui.debug("Casting Lightning Bolt [ST 5+ Maelstrom]") return true end
- end
- if ((unit.distance(units.dyn40) > 5 and (not unit.moving(units.dyn40) or not unit.facing(units.dyn40,"player"))) or unit.distance(units.dyn40) > 20) and not unit.moving() then
- if cast.lightningBolt() then ui.debug("Casting Lightning Bolt [ST Out of Range]") return true end
- end
- end
- -- Sundering
- -- sundering,if=raid_event.adds.in>=40
- if ui.alwaysCdNever("Sundering") and #enemies.yards11r > 0 and cast.able.sundering("player","rect",1,11) then
- if cast.sundering("player","rect",1,11) then ui.debug("Casting Sundering [ST]") return true end
- end
- -- Fire Nova
- -- fire_nova,if=talent.swirling_maelstrom.enabled&active_dot.flame_shock
- if #enemies.yards8 > 0 and cast.able.fireNova("player","aoe",1,8) and talent.swirlingMaelstrom and debuff.flameShock.count() > 1 then
- if cast.fireNova("player","aoe",1,8) then ui.debug("Casting Fire Nova [ST Swirling Maelstrom]") return true end
- end
- -- Frost Shock
- -- frost_shock
- if cast.able.frostShock() then
- if cast.frostShock() then ui.debug("Casting Frost Shock [ST]") return true end
- end
- -- Crash Lightning
- -- crash_lightning
- if #enemies.yards8c > 0 and cast.able.crashLightning("player","cone",1,8) then
- if cast.crashLightning("player","cone",1,8) then ui.debug("Casting Crash Lightning [ST]") return true end
- end
- -- Fire Nova
- -- fire_nova,if=active_dot.flame_shock
- if #enemies.yards8 > 0 and cast.able.fireNova("player","aoe",1,8) and debuff.flameShock.count() > 1 then
- if cast.fireNova("player","aoe",1,8) then ui.debug("Casting Fire Nova [ST]") return true end
- end
- -- Earth Elemental
- -- earth_elemental
- if ui.alwaysCdNever("Earth Elemental") and cast.able.earthElemental() and ((#enemies.yards8 > 1 and unit.ttd("target") > 15) or unit.isDummy("target")) then
- if cast.earthElemental() then ui.debug("Casting Earth Elemental [ST]") return true end
- end
- -- Primal Strike
- if cast.able.primalStrike() and unit.level() < 20 then
- if cast.primalStrike() then ui.debug("Casting Primal Strike [ST]") return true end
- end
- -- Lightning Bolt
- if cast.able.lightningBolt() and canLightning() then
- if cast.lightningBolt() then var.fillLightning = true ui.debug("Casting Lightning Bolt [ST - Filler]") return true end
- end
- br.debug.cpu:updateDebug(startTime,"rotation.profile.single")
-end -- End Action List - Single
--- Action List - PreCombat
-actionList.PreCombat = function()
- local startTime = br._G.debugprofilestop()
- if not unit.inCombat() and not (unit.flying() or unit.mounted() or unit.taxi()) then
- -- Flask Up Module
- -- flask
- module.FlaskUp("Agility")
- -- Augmentation
- -- augmentation
- -- Windfury Weapon
- -- windfury_weapon
- if ui.checked("Windfury Weapon") and cast.able.windfuryWeapon() and not unit.weaponImbue.exists(5401) then
- if cast.windfuryWeapon("player") then ui.debug("Casting Windfury Weapon [Main Hand]") return true end
- end
- -- Flametongue Weapon
- -- flametongue_weapon
- if ui.checked("Flametongue Weapon") and cast.able.flametongueWeapon() and unit.dualWielding() and not unit.weaponImbue.exists(5400,true) then
- if cast.flametongueWeapon("player") then ui.debug("Casting Flametongue Weapon [Off Hand]") return true end
- end
- -- Skyfury
- if ui.checked("Skyfury") and cast.able.skyfury() and not buff.skyfury.exists() then
- if cast.skyfury("player") then ui.debug("Casting Skyfury") return true end
- end
- -- Lightning Shield
- -- lightning_shield
- if (ui.value("Shields Up") == 1 or (ui.value("Shields Up") == 2 and not talent.earthShield)) and cast.able.lightningShield() and not buff.lightningShield.exists() then
- if cast.lightningShield("player") then ui.debug("Casting Lightning Shield") return true end
- end
- if ui.checked("Pre-Pull Timer") and ui.pullTimer() <= ui.value("Pre-Pull Timer") then
- -- Potion
- -- potion
- -- if ui.checked("Potion") and br.canUseItem(142117) and unit.instance("raid") then
- -- if feralSpiritRemain > 5 and not buff.prolongedPower.exists() then
- -- br.useItem(142117)
- -- end
- -- end
- end -- End Pre-Pull
- if unit.valid("target") then
- -- Feral Lunge
- if ui.checked("Feral Lunge") and cast.able.feralLunge() then
- if cast.feralLunge("target") then ui.debug("Casting Feral Lunge [Pull]") return true end
- end
- -- Lightning Bolt
- if ui.checked("Lightning Bolt Out of Combat") and cast.able.lightningBolt() and not unit.moving()
- and unit.distance("target") >= 10 and (not ui.checked("Feral Lunge") or not talent.feralLunge
- or cd.feralLunge.remain() > unit.gcd(true) or not cast.able.feralLunge())
- and cast.timeSinceLast.lightningBolt() > cast.time.lightningBolt() + unit.gcd(true)
- -- and (not ui.checked("Ghost Wolf") or unit.distance("target") <= 20 or not buff.ghostWolf.exists())
- then
- if cast.lightningBolt("target") then ui.debug("Casting Lightning Bolt [Pull]") return true end
- end
- -- Start Attack
- if unit.distance("target") < 5 then
- if cast.able.autoAttack("target") then
- if cast.autoAttack("target") then ui.debug("Casting Auto Attack [Pull]") return true end
- end
- end
- end
- end -- End No Combat
- br.debug.cpu:updateDebug(startTime,"rotation.profile.precombat")
-end -- End Action List - PreCombat
-
-----------------
---- ROTATION ---
-----------------
-local function runRotation()
- --------------
- --- Locals ---
- --------------
- -- BR API
- buff = br.player.buff
- cast = br.player.cast
- cd = br.player.cd
- charges = br.player.charges
- debuff = br.player.debuff
- enemies = br.player.enemies
- equiped = br.player.equiped
- module = br.player.module
- runeforge = br.player.runeforge
- talent = br.player.talent
- ui = br.player.ui
- unit = br.player.unit
- units = br.player.units
- var = br.player.variables
-
- -- Dynamic Units
- units.get(5)
- -- units.get(8)
- -- units.get(20)
- units.get(40)
-
- -- Enemies Lists
- enemies.get(5)
- enemies.get(8)
- enemies.get(8,"player",false,true)
- enemies.cone.get(180,8,false)
- enemies.get(10)
- enemies.rect.get(10,11,false)
- enemies.get(20)
- enemies.get(30)
- enemies.get(40,"player",false,true)
- enemies.get(40)
-
- var.lowestFlameShock = debuff.flameShock.lowest(40,"remain") or "target"
- var.lowestLashingFlames = debuff.lashingFlames.lowest(5,"remain") or "target"
- var.overflowingMaelstrom = talent.overflowingMaelstrom and 1 or 0
-
- if var.fillLightning == nil then var.fillLightning = false end
- if unit.distance("target") < 5 and buff.maelstromWeapon.stack() < 5 and not var.fillLightning then
- -- Cancel Lightning Bolt in Melee
- if cast.current.lightningBolt() and not canLightning() then
- if cast.cancel.lightningBolt() then ui.debug("Canceled Lightning Bolt Cast [Melee Range]") end
- end
- -- Cancel Chain Lightning Bolt in Melee
- if cast.current.chainLightning() and not canLightning(true) then
- if cast.cancel.chainLightning() then ui.debug("Canceled Chain Lightning Cast [Melee Range]") end
- end
- end
-
-
- var.healUnit = ui.value("Heal Target") == 1 and unit.lowest(40) or "player"
- var.healHP = unit.hp(var.healUnit)
- var.unitsNeedingHealing = 0
- if ui.checked("Use HST While Solo") and br.getHP("player") <= ui.value("Healing Stream Totem") then
- var.unitsNeedingHealing = var.unitsNeedingHealing + 1
- end
- if #br.friend > 1 then
- for i = 1, #br.friend do
- local thisFriend = br.friend[i].unit
- local thisDistance = unit.distance(thisFriend)
- if not unit.isUnit(thisFriend,"player") and thisDistance < 40 and unit.hp(thisFriend) <= ui.value("Healing Stream Totem") then
- var.unitsNeedingHealing = var.unitsNeedingHealing + 1
- end
- end
- end
-
- if var.profileStop == nil then var.profileStop = false end
-
- ---------------------
- --- Begin Profile ---
- ---------------------
- -- Profile Stop | Pause
- if not unit.inCombat() and not unit.exists("target") and var.profileStop then
- var.profileStop = false
- elseif (unit.inCombat() and var.profileStop==true) or ui.pause() or unit.mounted() or unit.flying() or ui.mode.rotation == 4 then
- return true
- else
- -----------------------
- --- Extras Rotation ---
- -----------------------
- if actionList.Extras() then return true end
- --------------------------
- --- Defensive Rotation ---
- --------------------------
- if actionList.Defensive() then return true end
- ---------------------------
- --- Pre-Combat Rotation ---
- ---------------------------
- if actionList.PreCombat() then return true end
- --------------------------
- --- In Combat Rotation ---
- --------------------------
- if unit.inCombat() and unit.valid("target") and not var.profileStop then
- ------------------------------
- --- In Combat - Interrupts ---
- ------------------------------
- if actionList.Interrupts() then return true end
- ------------------------
- --- In Combat - Main ---
- ------------------------
- -- bloodlust,line_cd=600
- -- potion,if=(talent.ascendance.enabled&raid_event.adds.in>=90&cooldown.ascendance.remains<10)|(talent.hot_hand.enabled&buff.molten_weapon.up)|buff.icy_edge.up|(talent.stormflurry.enabled&buff.crackling_surge.up)|active_enemies>1|fight_remains<30
- -- Feral Lunge
- if ui.checked("Feral Lunge") and cast.able.feralLunge() and unit.distance("target") > 10 then
- if cast.feralLunge("target") then ui.debug("Casting Feral Lunge") return true end
- end
- -- Start Attack
- if unit.distance("target") <= 5 then
- if cast.able.autoAttack("target") then
- if cast.autoAttack("target") then ui.debug("Casting Auto Attack") return true end
- end
- end
- -- Basic Trinkets Module
- if #enemies.yards8f > 0 then
- module.BasicTrinkets()
- end
- -- Racials
- if ui.alwaysCdNever("Racial") and cast.able.racial() then
- if cast.racial() then ui.debug("Casting Racial") return true end
- end
- -- Feral Spirit
- -- feral_spirit
- if ui.alwaysCdNever("Feral Spirit") and cast.able.feralSpirit() then
- if cast.feralSpirit() then ui.debug("Casting Feral Spirit") return true end
- end
- -- Ascendance
- -- ascendance
- -- ascendance,if=(ti_lightning_bolt&active_enemies=1&raid_event.adds.in>=90)|(ti_chain_lightning&active_enemies>1)
- if ui.alwaysCdNever("Ascendance") and cast.able.ascendance() and ((#enemies.yards8 > 1 and unit.ttd("target") > 15) or unit.isDummy("target")) then
- if cast.ascendance() then
- ui.debug("Casting Ascendance")
- return true
- end
- end
- -- Doom Winds
- -- doom_winds,if=raid_event.adds.in>=90|active_enemies>1
- if cast.able.doomWinds() then
- if cast.doomWinds() then ui.debug("Casting Doom Winds") return true end
- end
- -- Call Action List - Single
- -- call_action_list,name=single,if=active_enemies=1
- if ui.useST(8,2) then
- if actionList.Single() then return true end
- end
- -- Call Action List - AOE
- -- call_action_list,name=aoe,if=active_enemies>1
- if ui.useAOE(8,2) then
- if actionList.AOE() then return true end
- end
- end --End In Combat
- end --End Rotation Logic
-end -- End runRotation
-local id = 263
-if br.rotations[id] == nil then br.rotations[id] = {} end
-br._G.tinsert(br.rotations[id],{
- name = rotationName,
- toggles = createToggles,
- options = createOptions,
- run = runRotation,
-})
diff --git a/Rotations/Shaman/Enhancement/EnhancementCuteOne-MOP.lua b/Rotations/Shaman/Enhancement/EnhancementCuteOne-MOP.lua
new file mode 100644
index 0000000000..62868aaaff
--- /dev/null
+++ b/Rotations/Shaman/Enhancement/EnhancementCuteOne-MOP.lua
@@ -0,0 +1,1023 @@
+-------------------------------------------------------
+-- Author = CuteOne
+-- Patch = 11.0.2
+-- Patch should be the latest patch you've updated the rotation for (i.e., 9.2.5)
+-- Coverage = 100%
+-- Coverage should be your estimated percent coverage for class mechanics (i.e., 100%)
+-- Status = Limited
+-- Status should be one of: Full, Limited, Sporadic, Inactive, Unknown
+-- Readiness = NoRaid
+-- Readiness should be one of: Raid, NoRaid, Basic, Development, Untested
+-------------------------------------------------------
+-- Required: Fill above fields to populate README.md --
+-------------------------------------------------------
+
+local rotationName = "CuteOne"
+---------------
+--- Toggles ---
+---------------
+local function createToggles()
+ -- Rotation Button
+ local RotationModes = {
+ [1] = { mode = "Auto", value = 1 , overlay = "Automatic Rotation", tip = "Swaps between Single and Multiple based on number of targets in range.", highlight = 1, icon = br.player.spells.stormstrike },
+ [2] = { mode = "Mult", value = 2 , overlay = "Multiple Target Rotation", tip = "Multiple target rotation used.", highlight = 0, icon = br.player.spells.fireNova },
+ [3] = { mode = "Sing", value = 3 , overlay = "Single Target Rotation", tip = "Single target rotation used.", highlight = 0, icon = br.player.spells.stormstrike },
+ [4] = { mode = "Off", value = 4 , overlay = "DPS Rotation Disabled", tip = "Disable DPS Rotation", highlight = 0, icon = br.player.spells.healingSurge }
+ };
+ br.ui:createToggle(RotationModes, "Rotation", 1, 0)
+ -- Cooldown Button
+ local CooldownModes = {
+ [1] = { mode = "Auto", value = 1 , overlay = "Cooldowns Automated", tip = "Automatic Cooldowns - Boss Detection.", highlight = 1, icon = br.player.spells.feralSpirit },
+ [2] = { mode = "On", value = 1 , overlay = "Cooldowns Enabled", tip = "Cooldowns used regardless of target.", highlight = 0, icon = br.player.spells.feralSpirit },
+ [3] = { mode = "Off", value = 3 , overlay = "Cooldowns Disabled", tip = "No Cooldowns will be used.", highlight = 0, icon = br.player.spells.feralSpirit }
+ };
+ br.ui:createToggle(CooldownModes,"Cooldown",2,0)
+ -- Defensive Button
+ local DefensiveModes = {
+ [1] = { mode = "On", value = 1 , overlay = "Defensive Enabled", tip = "Includes Defensive Cooldowns.", highlight = 1, icon = br.player.spells.astralShift },
+ [2] = { mode = "Off", value = 2 , overlay = "Defensive Disabled", tip = "No Defensives will be used.", highlight = 0, icon = br.player.spells.astralShift }
+ };
+ br.ui:createToggle(DefensiveModes,"Defensive",3,0)
+ -- Interrupt Button
+ local InterruptModes = {
+ [1] = { mode = "On", value = 1 , overlay = "Interrupts Enabled", tip = "Includes Basic Interrupts.", highlight = 1, icon = br.player.spells.windShear },
+ [2] = { mode = "Off", value = 2 , overlay = "Interrupts Disabled", tip = "No Interrupts will be used.", highlight = 0, icon = br.player.spells.windShear }
+ };
+ br.ui:createToggle(InterruptModes,"Interrupt",4,0)
+end
+
+---------------
+--- OPTIONS ---
+---------------
+local function createOptions()
+ local optionTable
+
+ local function rotationOptions()
+ local section
+ local alwaysCdNever = {"|cff00FF00Always","|cffFFFF00Cooldowns","|cffFF0000Never"}
+ -- General Options
+ section = br.ui:createSection(br.ui.window.profile, "General")
+ -- Dummy DPS Test
+ br.ui:createSpinner(section, "DPS Testing", 5, 5, 60, 5, "|cffFFFFFFSet to desired time for test in minuts. Min: 5 / Max: 60 / Interval: 5")
+ -- Pre-Pull Timer
+ br.ui:createSpinner(section, "Pre-Pull Timer", 5, 1, 10, 1, "|cffFFFFFFSet to desired time to start Pre-Pull (DBM Required). Min: 1 / Max: 10 / Interval: 1")
+ -- Ghost Wolf
+ br.ui:createDropdownWithout(section,"Ghost Wolf", {"|cff00FF00Always","|cffFFFF00OoC Only","|cffFF0000Never"}, 1, "|cffFFFFFFWhen to use Ghost Wolf.")
+ -- Feral Lunge
+ br.ui:createCheckbox(section,"Feral Lunge")
+ -- Lightning Bolt OOC
+ br.ui:createCheckbox(section,"Lightning Bolt Out of Combat")
+ -- Lightning Filler
+ br.ui:createCheckbox(section,"Lightning Filler","Select to use LB/CL when all other abilities are on CD and can cast before any available.")
+ -- Lightning Shield
+ br.ui:createCheckbox(section, "Lightning Shield")
+ -- Water Shield
+ br.ui:createSpinner(section, "Water Shield", 30, 0, 100, 5, "|cffFFFFFFMana percent to swap to Water Shield.")
+ -- Spirit Walk
+ br.ui:createCheckbox(section,"Spirit Walk")
+ -- Water Walking
+ br.ui:createCheckbox(section,"Water Walking")
+ -- Weapon Imbues
+ br.ui:createCheckbox(section,"Windfury Weapon")
+ br.ui:createCheckbox(section,"Flametongue Weapon")
+ br.ui:checkSectionState(section)
+ -- Cooldown Options
+ section = br.ui:createSection(br.ui.window.profile, "Cooldowns")
+ -- Ascendance
+ br.ui:createDropdownWithout(section,"Ascendance", alwaysCdNever, 1, "|cffFFFFFFWhen to use Ascendance.")
+ -- Ancestral Swiftness
+ br.ui:createDropdownWithout(section,"Ancestral Swiftness", alwaysCdNever, 1, "|cffFFFFFFWhen to use Ancestral Swiftness.")
+ -- Earth Elemental Totem
+ br.ui:createDropdownWithout(section,"Earth Elemental", alwaysCdNever, 1, "|cffFFFFFFWhen to use Earth Elemental.")
+ -- Elemental Mastery
+ br.ui:createDropdownWithout(section,"Elemental Mastery", alwaysCdNever, 1, "|cffFFFFFFWhen to use Elemental Mastery.")
+ -- Feral Spirit
+ br.ui:createDropdownWithout(section,"Feral Spirit", alwaysCdNever, 1, "|cffFFFFFFWhen to use Feral Spirit.")
+ -- Fire Elemental Totem
+ br.ui:createDropdownWithout(section,"Fire Elemental", alwaysCdNever, 1, "|cffFFFFFFWhen to use Fire Elemental Totem.")
+ -- Stormlash Totem
+ br.ui:createDropdownWithout(section,"Stormlash", alwaysCdNever, 1, "|cffFFFFFFWhen to use Stormlash Totem.")
+ br.ui:checkSectionState(section)
+ -- Defensive Options
+ section = br.ui:createSection(br.ui.window.profile, "Defensive")
+ -- Ancestral Spirit
+ br.ui:createDropdown(section, "Ancestral Spirit", {"|cffFFFF00Selected Target","|cffFF0000Mouseover Target"}, 1, "|ccfFFFFFFTarget to Cast On")
+ -- Astral Shift
+ br.ui:createSpinner(section, "Astral Shift", 40, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ -- Cleanse Spirit
+ br.ui:createDropdown(section, "Cleanse Spirit", {"|cff00FF00Player Only","|cffFFFF00Selected Target","|cffFF0000Mouseover Target"}, 1, "|ccfFFFFFFTarget to Cast On")
+ -- Healing Surge
+ br.ui:createSpinner(section, "Healing Surge", 50, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ br.ui:createSpinnerWithout(section, "Healing Surge OoC", 90, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ -- Auto/Insta-Heal
+ br.ui:createDropdownWithout(section, "Heal Target", { "|cffFFDD11LowestHP", "|cffFFDD11Player"}, 1, "|cffFFFFFFSelect Target to Heal")
+ br.ui:createDropdownWithout(section, "Instant Behavior", {"|cff00FF00Always","|cffFFFF00Combat Only","|cffFF0000Never"}, 2, "|cffFFFFFFSelect how to use Instant Heal proc.")
+ -- Healing Steam Totem
+ br.ui:createSpinner(section, "Healing Stream Totem", 35, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ br.ui:createCheckbox(section, "Use HST While Solo")
+ br.ui:createSpinnerWithout(section, "Healing Stream Totem - Min Units", 1, 0, 5, 1, "|cffFFFFFFNumber of Units below HP Level to Cast At")
+ -- Lightning Surge Totem
+ br.ui:createSpinner(section, "Capacitor Totem - HP", 30, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ br.ui:createSpinner(section, "Capacitor Totem - AoE", 5, 0, 10, 1, "|cffFFFFFFNumber of Units in 5 Yards to Cast At")
+ -- Purge
+ br.ui:createCheckbox(section,"Purge")
+ br.ui:checkSectionState(section)
+ -- Interrupt Options
+ section = br.ui:createSection(br.ui.window.profile, "Interrupts")
+ -- Wind Shear
+ br.ui:createCheckbox(section,"Wind Shear")
+ -- Hex
+ br.ui:createCheckbox(section,"Hex")
+ -- Lightning Surge Totem
+ br.ui:createCheckbox(section,"Capacitor Totem")
+ -- Interrupt Percentage
+ br.ui:createSpinnerWithout(section, "Interrupt At", 0, 0, 95, 5, "|cffFFFFFFCast Percent to Cast At")
+ br.ui:checkSectionState(section)
+ -- Toggle Key Options
+ section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
+ -- Single/Multi Toggle
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
+ -- Cooldown Key Toggle
+ br.ui:createDropdownWithout(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 3)
+ -- Defensive Key Toggle
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
+ -- Interrupts Key Toggle
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
+ br.ui:checkSectionState(section)
+ end
+ optionTable = {{
+ [1] = "Rotation Options",
+ [2] = rotationOptions,
+ }}
+ return optionTable
+end
+
+--------------
+--- Locals ---
+--------------
+-- BR API Locals
+local buff
+local cast
+local cd
+local debuff
+local enemies
+local equiped
+local glyph
+local mana
+local spell
+local talent
+local totem
+local ui
+local unit
+local units
+local var
+
+-- General API Locals
+local actionList = {}
+
+-----------------
+--- Functions ---
+-----------------
+-- Cancel a filler lightning cast if it's actively being cast
+local function cancelFillerLightning()
+ if var.fillerLightningBolt and cast.current.lightningBolt() then
+ if cast.cancel.lightningBolt() then
+ ui.debug("Canceled Filler Lightning Bolt")
+ var.fillerLightningBolt = false
+ return true
+ end
+ end
+ if var.fillerChainLightning and cast.current.chainLightning() then
+ if cast.cancel.chainLightning() then
+ ui.debug("Canceled Filler Chain Lightning")
+ var.fillerChainLightning = false
+ return true
+ end
+ end
+ return false
+end
+-- Calculate time until next priority ability is ready
+local function getTimeToNextAbility()
+ if not unit.exists("target") then return 0 end
+
+ local dist = unit.distance("target") or 99
+ local timeToNext = 99
+
+ -- Helper to check cooldown and distance
+ local function checkAbility(spellKnown, abilityCD, maxRange)
+ -- Only check if spell is known and we're in range
+ if spellKnown and dist <= maxRange and abilityCD and abilityCD.exists() then
+ local remain = abilityCD.remain()
+ -- If ability is ready now or will be ready very soon, return 0
+ if remain <= 0 then
+ timeToNext = 0
+ else
+ timeToNext = math.min(timeToNext, remain)
+ end
+ end
+ end
+
+ -- Check all priority abilities based on what's known and in range
+ if spell.stormstrike.known() and not buff.ascendance.exists() then
+ checkAbility(true, cd.stormstrike, 5)
+ end
+ if spell.stormblast.known() and buff.ascendance.exists() then
+ checkAbility(true, cd.stormblast, 5)
+ end
+ checkAbility(spell.lavaLash.known(), cd.lavaLash, 5)
+ checkAbility(spell.primalStrike.known(), cd.primalStrike, 5)
+ checkAbility(spell.flameShock.known(), cd.flameShock, 40)
+ checkAbility(spell.earthShock.known(), cd.earthShock, 40)
+ checkAbility(spell.frostShock.known(), cd.frostShock, 40)
+ checkAbility(spell.unleashElements.known(), cd.unleashElements, 30)
+
+ if talent.elementalBlast then
+ checkAbility(true, cd.elementalBlast, 40)
+ end
+ if spell.fireNova.known() and debuff.flameShock.count() >= 1 then
+ checkAbility(true, cd.fireNova, 10)
+ end
+
+ return timeToNext
+end
+
+-- Check if we can safely cast filler Lightning Bolt/Chain Lightning
+local function canCastFillerLightning(isChainLightning, castTimeOverride)
+ if not ui.checked("Lightning Filler") then return false end
+ if not unit.exists("target") or not unit.inCombat() then return false end
+
+ -- Don't filler cast if we're in melee range (< 5 yards)
+ local dist = unit.distance("target") or 99
+ if dist < 5 then return false end
+
+ -- Calculate cast time (GCD doesn't matter since it happens AFTER the cast)
+ local castTime = castTimeOverride or (isChainLightning and cast.time.chainLightning() or cast.time.lightningBolt())
+
+ -- Get time until next priority ability
+ local timeToNext = getTimeToNextAbility()
+
+ -- Only allow filler cast if we have enough time to complete the cast
+ -- Add a small buffer (0.2s) to account for latency and ensure priority spell can be cast immediately
+ return timeToNext > (castTime + 0.2)
+end
+
+-- local function canLightning(aoe)
+-- local level = unit.level()
+-- if not ui.checked("Lightning Filler") or level < 10 then return false end
+-- local timeTillLightning = (aoe and talent.chainLightning) and cast.time.chainLightning() or cast.time.lightningBolt()
+
+-- local earthShock = spell.earthShock.known() and cd.earthShock.remain() or 99
+-- local elementalBlast = talent.elementalBlast and cd.elementalBlast.remain() or 99
+-- local fireNova = spell.fireNova.known() and cd.fireNova.remain() or 99
+-- local flameShock = spell.flameShock.known() and cd.flameShock.remain() or --[[debuff.flameShock.exists() and debuff.flameShock.remain(units.dyn25) or]] 99
+-- local frostShock = spell.frostShock.known() and cd.frostShock.remain() or 99
+-- local lavaLash = spell.lavaLash.known() and cd.lavaLash.remain() or 99
+-- local primalStrike = (level < 26 and not spell.stormStrike.known()) and cd.primalStrike.remain() or 99
+-- local stormblast = (spell.stormblast.known() and buff.ascendance.exists()) and cd.stormblast.remain() or 99
+-- local stormstrike = (spell.stormstrike.known() and not buff.ascendance.exists()) and cd.stormstrike.remain() or 99
+
+-- return math.min(earthShock,elementalBlast,fireNova,flameShock,frostShock,lavaLash,primalStrike,stormblast,stormstrike) > timeTillLightning
+-- end
+
+--------------------
+--- Action Lists ---
+--------------------
+
+-- Action List - Extras
+actionList.Extras = function()
+ local startTime = br._G.debugprofilestop()
+ -- * Dummy Test
+ if ui.checked("DPS Testing") then
+ if unit.exists("target") then
+ if unit.combatTime() >= (tonumber(ui.value("DPS Testing"))*60) and unit.isDummy() then
+ unit.stopAttack()
+ unit.clearTarget()
+ ui.print(tonumber(ui.value("DPS Testing")) .." Minute Dummy Test Concluded - Profile Stopped")
+ var.profileStop = true
+ end
+ end
+ end
+ -- * Ghost Wolf
+ if (ui.value("Ghost Wolf") == 1 or (ui.value("Ghost Wolf") == 2 and not unit.inCombat())) and cast.able.ghostWolf() and not (unit.mounted() or unit.flying()) then
+ if ((#enemies.yards20 == 0 and not unit.inCombat()) or (#enemies.yards10 == 0 and unit.inCombat())) and unit.moving("player") and not buff.ghostWolf.exists() then
+ if cast.ghostWolf() then ui.debug("Casting Ghost Wolf [Extra]") return true end
+ end
+ end
+ -- * Purge
+ if ui.checked("Purge") and cast.able.purge() and cast.dispel.purge("target") and not unit.isBoss() and unit.exists("target") then
+ if cast.purge() then ui.debug("Casting Purge [Extra]") return true end
+ end
+ -- * Spirit Walk
+ if ui.checked("Spirit Walk") and cast.able.spiritWalk() and unit.moving() then
+ if cast.spiritWalk() then ui.debug("Casting Spirit Walk [Extra]") return true end
+ end
+ -- * Water Walking
+ if ui.checked("Water Walking") and cast.able.waterWalking() and not unit.inCombat() and not buff.waterWalking.exists() then
+ if cast.waterWalking() then ui.debug("Casting Water Walking [Extra]") return true end
+ end
+ -- * Windfury Weapon
+ -- windfury_weapon,weapon=main
+ if ui.checked("Windfury Weapon") and cast.able.windfuryWeapon() and not unit.weaponImbue.exists(283) and unit.level() >= 30 then
+ if cast.windfuryWeapon("player") then ui.debug("Casting Windfury Weapon - Main [Precombat]") return true end
+ end
+ -- * Flametongue Weapon
+ -- flametongue_weapon,weapon=main
+ if ui.checked("Flametongue Weapon") and cast.able.flametongueWeapon() and not unit.weaponImbue.exists(5) and unit.level() < 30 then
+ if cast.flametongueWeapon("player") then ui.debug("Casting Flametongue Weapon - Main [Precombat]") return true end
+ end
+ -- flametongue_weapon,weapon=off
+ if ui.checked("Flametongue Weapon") and cast.able.flametongueWeapon() and unit.dualWielding()
+ and not unit.weaponImbue.exists(5,true)
+ then
+ if cast.flametongueWeapon("player") then ui.debug("Casting Flametongue Weapon - Off [Precombat]") return true end
+ end
+ -- * Lightning Shield
+ -- lightning_shield,if=!buff.lightning_shield.up
+ if ui.checked("Lightning Shield") and cast.able.lightningShield()
+ and not buff.lightningShield.exists() and (mana.percent() >= math.min(100, ui.value("Water Shield") * 2) or not ui.checked("Water Shield"))
+ then
+ if cast.lightningShield("player") then ui.debug("Casting Lightning Shield [Precombat]") return true end
+ end
+ -- * Water Shield
+ if cast.able.waterShield() and ui.checked("Water Shield")
+ and mana.percent() <= ui.value("Water Shield") and not buff.waterShield.exists()
+ then
+ if cast.waterShield("player") then ui.debug("Casting Water Shield [Extra]") return true end
+ end
+ -- * Totemic Recall
+ if cast.able.totemicRecall() and totem.exists() and ((not unit.inCombat() and #enemies.yards25nc == 0)
+ or (unit.inCombat() and math.max(totem.fire.distance(), totem.earth.distance(), totem.water.distance(), totem.air.distance()) > 25))
+ then
+ if cast.totemicRecall() then ui.debug("Casting Totemic Recall [Extra]") return true end
+ end
+ br.debug.cpu:updateDebug(startTime,"rotation.profile.extras")
+end -- End Action List - Extras
+
+-- Action List - Defensive
+actionList.Defensive = function()
+ local startTime = br._G.debugprofilestop()
+ if ui.useDefensive() then
+ -- * Ancestral Spirit
+ if ui.checked("Ancestral Spirit") and cast.timeSinceLast.ancestralSpirit() > 5 then
+ if ui.value("Ancestral Spirit")==1 and cast.able.ancestralSpirit("target","dead") and unit.player("target") then
+ if cast.ancestralSpirit("target","dead") then ui.debug("Casting Ancestral Spirit - Target [Defensive]") return true end
+ end
+ if ui.value("Ancestral Spirit")==2 and cast.able.ancestralSpirit("mouseover","dead") and unit.player("mouseover") then
+ if cast.ancestralSpirit("mouseover","dead") then ui.debug("Casting Ancestral Spirit - Mouseover [Defensive]") return true end
+ end
+ end
+ -- * Astral Shift
+ if ui.checked("Astral Shift") and cast.able.astralShift() and unit.hp() <= ui.value("Astral Shift") and unit.inCombat() then
+ if cast.astralShift() then ui.debug("Casting Astral Shift [Defensive]") return true end
+ end
+ -- * Capacitor Totem
+ if ui.checked("Capacitor Totem - HP") and cast.able.capacitorTotem("player") and cd.capacitorTotem.ready()
+ and unit.hp() <= ui.value("Capacitor Totem - HP") and unit.inCombat() and #enemies.yards5 > 0
+ then
+ if cast.capacitorTotem("player") then ui.debug("Casting Capacitor Totem - Low HP [Defensive]") return true end
+ end
+ if ui.checked("Capacitor Totem - AoE") and cast.able.capacitorTotem("best",nil,ui.value("Capacitor Totem - AoE"),8) and cd.capacitorTotem.ready()
+ and #enemies.yards5 >= ui.value("Capacitor Totem - AoE") and unit.inCombat()
+ then
+ if cast.capacitorTotem("best",nil,ui.value("Capacitor Totem - AoE"),8) then ui.debug("Casting Capacitor Totem - AOE [Defensive]") return true end
+ end
+ -- * Cleanse Spirit
+ if ui.checked("Cleanse Spirit") then
+ if ui.value("Cleanse Spirit")==1 and cast.able.cleanseSpirit("player") and cast.dispel.cleanseSpirit("player") then
+ if cast.cleanseSpirit("player") then ui.debug("Casting Cleanse Spirit - Player [Defensive]") return true end
+ end
+ if ui.value("Cleanse Spirit")==2 and cast.able.cleanseSpirit("target") and cast.dispel.cleanseSpirit("target") then
+ if cast.cleanseSpirit("target") then ui.debug("Casting Cleanse Spirit - Target [Defensive]") return true end
+ end
+ if ui.value("Cleanse Spirit")==3 and cast.able.cleanseSpirit("mouseover") and cast.dispel.cleanseSpirit("mouseover") then
+ if cast.cleanseSpirit("mouseover") then ui.debug("Casting Cleanse Spirit - Mouseover [Defensive]") return true end
+ end
+ end
+ -- * Healing Surge
+ if ui.checked("Healing Surge") and cast.able.healingSurge(var.healUnit) and not (unit.mounted() or unit.flying())
+ and (ui.value("Heal Target") ~= 1 or (ui.value("Heal Target") == 1
+ and unit.distance(br.engines.healingEngine.friend[1].unit) < 40)) and not cast.current.healingSurge()
+ then
+ if not unit.inCombat() then
+ -- Lowest Party/Raid or Player
+ if (var.healHP <= ui.value("Healing Surge OoC") and not unit.moving())
+ and (buff.maelstromWeapon.stack() == 0 or ui.value("Instant Behavior") == 1)
+ then
+ if cast.healingSurge(var.healUnit) then ui.debug("Casting Healing Surge - Ooc on "..unit.name(var.healUnit).." [Defensive]") return true end
+ end
+ elseif unit.inCombat() and (not unit.moving() or buff.maelstromWeapon.stack() >= 5) then
+ -- Lowest Party/Raid or Player
+ if var.healHP <= ui.value("Healing Surge") then
+ if ui.value("Instant Behavior") == 1 or (ui.value("Instant Behavior") == 2 and buff.maelstromWeapon.stack() >= 5) or (ui.value("Instant Behavior") == 3 and buff.maelstromWeapon.stack() == 0) then
+ if buff.maelstromWeapon.stack() >= 5 then
+ if cast.healingSurge(var.healUnit) then ui.debug("Casting Healing Surge - IC Instant on "..unit.name(var.healUnit).." [Defensive]") return true end
+ else
+ if cast.healingSurge(var.healUnit) then ui.debug("Casting Healing Surge - IC Long on "..unit.name(var.healUnit).." [Defensive]") return true end
+ end
+ end
+ end
+ end
+ end
+ -- * Healing Stream Totem
+ if ui.checked("Healing Stream Totem") and cast.able.healingStreamTotem("player")
+ and var.unitsNeedingHealing >= ui.value("Healing Stream Totem - Min Units")
+ then
+ if cast.healingStreamTotem("player") then ui.debug("Casting Healing Stream Totem [Defensive]") return true end
+ end
+ end -- End Defensive Toggle
+ br.debug.cpu:updateDebug(startTime,"rotation.profile.defensive")
+end -- End Action List - Defensive
+
+-- Action List - Interrupts
+actionList.Interrupt = function()
+ -- * Wind Shear
+ if ui.useInterrupt() and ui.checked("Wind Shear") and cast.able.windShear() then
+ local thisUnit
+ for i = 1, #enemies.yards30 do
+ thisUnit = enemies.yards30[i]
+ if unit.interruptable(thisUnit, ui.value("Interrupt At")) then
+ if cast.windShear(thisUnit) then
+ ui.debug("Casting Wind Shear on " .. unit.name(thisUnit) .. " [Interrupt]")
+ return true
+ end
+ end
+ end
+ end
+end -- End Action List - Interrupts
+
+-- Action List - AOE
+actionList.AOE = function()
+ local startTime = br._G.debugprofilestop()
+ -- * Fire Nova
+ -- fire_nova,if=active_flame_shock>=4
+ if cast.able.fireNova("player","aoe",1,10) and debuff.flameShock.count() >= 4 then
+ if cast.fireNova("player","aoe",1,10) then ui.debug("Casting Fire Nova - 4+ Flame Shock [AOE]") return true end
+ end
+ -- * Wait
+ -- wait,sec=cooldown.fire_nova.remains,if=active_flame_shock>=4&cooldown.fire_nova.remains<0.67
+ if debuff.flameShock.count() >= 4 and cd.fireNova.remain() < 0.67 then
+ return true
+ end
+ -- * Magma Totem
+ -- magma_totem,if=active_enemies>5&!totem.fire.active
+ if #enemies.yards8 > 5 and cast.able.magmaTotem("player")
+ and (not totem.fire.magma.exists() or totem.fire.magma.distance() > 8)
+ and unit.standingTime() > 1
+ then
+ if cast.magmaTotem("player") then ui.debug("Casting Magma Totem - 5+ Enemies [AOE]") return true end
+ end
+ -- * Searing Totem
+ -- searing_totem,if=active_enemies<=5&!totem.fire.active
+ if #enemies.yards8 <= 5 and cast.able.searingTotem("player")
+ and not totem.fire.searing.exists() and unit.standingTime() > 1
+ then
+ if cast.searingTotem("player") then ui.debug("Casting Searing Totem [AOE]") return true end
+ end
+ -- * Lava Lash
+ -- lava_lash,if=dot.flame_shock.ticking
+ for i = 1, #enemies.yards5 do
+ local thisUnit = enemies.yards5[i]
+ if cast.able.lavaLash(thisUnit) and debuff.flameShock.exists(thisUnit) then
+ if cast.lavaLash(thisUnit) then ui.debug("Casting Lava Lash on "..unit.name(thisUnit).." [AOE]") return true end
+ end
+ end
+ -- * Elemental Blast
+ -- elemental_blast,if=talent.elemental_blast.enabled&buff.maelstrom_weapon.react>=1
+ if cast.able.elementalBlast() and talent.elementalBlast and buff.maelstromWeapon.stack() >= 1 then
+ if cast.elementalBlast() then ui.debug("Casting Elemental Blast [AOE]") return true end
+ end
+ -- * Chain Lightning
+ -- chain_lightning,if=active_enemies>=2&buff.maelstrom_weapon.react>=3
+ if cast.able.chainLightning() and #enemies.yards8t >= 2 and buff.maelstromWeapon.stack() >= 3 then
+ if cast.chainLightning() then
+ var.fillerChainLightning = false
+ ui.debug("Casting Chain Lightning - 3+ Maelstrom [AOE]")
+ return true
+ end
+ end
+ -- * Unleash Elements
+ -- unleash_elements
+ if cast.able.unleashElements() then
+ if cast.unleashElements() then ui.debug("Casting Unleash Elements [AOE]") return true end
+ end
+ -- * Flame Shock
+ -- flame_shock,cycle_targets=1,if=!ticking
+ for i = 1, #enemies.yards5 do
+ local thisUnit = enemies.yards5[i]
+ if cast.able.flameShock(thisUnit) and not debuff.flameShock.exists(thisUnit) then
+ if cast.flameShock(thisUnit) then ui.debug("Casting Flame Shock on "..unit.name(thisUnit).." [AOE]") return true end
+ end
+ end
+ -- * Stormblast
+ -- stormblast
+ if cast.able.stormblast() and buff.ascendance.exists() then
+ if cast.stormblast() then ui.debug("Casting Stormblast [AOE]") return true end
+ end
+ -- * Fire Nova
+ -- fire_nova,if=active_flame_shock>=3
+ if cast.able.fireNova("player","aoe",1,10) and debuff.flameShock.count() >= 3 then
+ if cast.fireNova("player","aoe",1,10) then ui.debug("Casting Fire Nova - 3+ Flame Shock [AOE]") return true end
+ end
+ -- * Chain Lightning
+ -- chain_lightning,if=active_enemies>=2&buff.maelstrom_weapon.react>=1
+ if cast.able.chainLightning() and #enemies.yards8t >= 2 and buff.maelstromWeapon.stack() >= 1 then
+ if cast.chainLightning() then
+ var.fillerChainLightning = false
+ ui.debug("Casting Chain Lightning [AOE]")
+ return true
+ end
+ end
+ -- * Stormstrike
+ -- stormstrike
+ if cast.able.stormstrike() and not buff.ascendance.exists() and unit.level() >= 26 then
+ if cast.stormstrike() then ui.debug("Casting Stormstrike [AOE]") return true end
+ end
+ -- * Primal Strike
+ -- primal_strike
+ if cast.able.primalStrike() and unit.level() < 26 then
+ if cast.primalStrike() then ui.debug("Casting Primal Strike [AOE]") return true end
+ end
+ -- * Earth Shock
+ -- earth_shock,if=active_enemies<4
+ if cast.able.earthShock() and #enemies.yards8 < 4 then
+ if cast.earthShock() then ui.debug("Casting Earth Shock [AOE]") return true end
+ end
+ -- * Feral Spirit
+ -- feral_spirit
+ if ui.alwaysCdNever("Feral Spirit") and cast.able.feralSpirit() then
+ if cast.feralSpirit() then ui.debug("Casting Feral Spirit [AOE]") return true end
+ end
+ -- * Earth Elemental Totem
+ -- earth_elemental_totem,if=!active&cooldown.fire_elemental_totem.remains>=50
+ if ui.alwaysCdNever("Earth Elemental") and cast.able.earthElementalTotem("player") and not totem.earth.exists()
+ and cd.fireElementalTotem.remain() >= 50
+ then
+ if cast.earthElementalTotem("player") then ui.debug("Casting Earth Elemental Totem [AOE]") return true end
+ end
+ -- * Spiritwalkers Grace
+ -- spiritwalkers_grace,moving=1
+ if cast.able.spiritwalkersGrace() and unit.moving() then
+ if cast.spiritwalkersGrace() then ui.debug("Casting Spiritwalkers Grace [AOE]") return true end
+ end
+ -- * Fire Nova
+ -- fire_nova,if=active_flame_shock>=1
+ if cast.able.fireNova("player","aoe",1,10) and debuff.flameShock.count() >= 1 then
+ if cast.fireNova("player","aoe",1,10) then ui.debug("Casting Fire Nova - 1+ Flame Shock [AOE]") return true end
+ end
+ -- * Filler Chain Lightning
+ -- chain_lightning
+ if cast.able.chainLightning() and canCastFillerLightning(true) then
+ if cast.chainLightning() then
+ var.fillerChainLightning = true
+ ui.debug("Casting Chain Lightning - Filler [AOE]")
+ return true
+ end
+ end
+ br.debug.cpu:updateDebug(startTime,"rotation.profile.aoe")
+end -- End Action List - AOE
+
+-- Action List - Single
+actionList.Single = function()
+ local startTime = br._G.debugprofilestop()
+ -- * Searing Totem
+ -- searing_totem,if=!totem.fire.active
+ if cast.able.searingTotem("player") and not totem.fire.active() and unit.standingTime() > 1 then
+ if cast.searingTotem("player") then ui.debug("Casting Searing Totem [Single]") return true end
+ end
+ -- * Unleash Elements
+ -- unleash_elements,if=(talent.unleashed_fury.enabled|set_bonus.tier16_2pc_melee=1)
+ if cast.able.unleashElements() and (talent.unleashedFury or equiped.tier(16) >= 2) then
+ if cast.unleashElements() then ui.debug("Casting Unleash Elements - T16 2pc [Single]") return true end
+ end
+ -- * Elemental Blast
+ -- elemental_blast,if=talent.elemental_blast.enabled&buff.maelstrom_weapon.react>=1
+ if cast.able.elementalBlast() and talent.elementalBlast and buff.maelstromWeapon.stack() >= 1 then
+ if cast.elementalBlast() then ui.debug("Casting Elemental Blast [Single]") return true end
+ end
+ -- * Lightning Bolt
+ -- lightning_bolt,if=buff.maelstrom_weapon.react=5
+ if cast.able.lightningBolt() and buff.maelstromWeapon.stack() == 5 then
+ if cast.lightningBolt() then
+ var.fillerLightningBolt = false
+ ui.debug("Casting Lightning Bolt - 5 Stack Maelstrom [Single]")
+ return true
+ end
+ end
+ -- * Feral Spirit
+ -- feral_spirit,if=set_bonus.tier15_4pc_melee=1
+ if ui.alwaysCdNever("Feral Spirit") and cast.able.feralSpirit() and equiped.tier(15) >= 4 then
+ if cast.feralSpirit() then ui.debug("Casting Feral Spirit - T15 4pc [Single]") return true end
+ end
+ -- * Stormblast
+ -- stormblast
+ if cast.able.stormblast() and buff.ascendance.exists()then
+ if cast.stormblast() then ui.debug("Casting Stormblast [Single]") return true end
+ end
+ -- * Stormstrike
+ -- stormstrike
+ if cast.able.stormstrike() and not buff.ascendance.exists() and unit.level() >= 26 then
+ if cast.stormstrike() then ui.debug("Casting Stormstrike [Single]") return true end
+ end
+ -- * Primal Strike
+ -- primal_strike,if=level<26
+ if cast.able.primalStrike() and unit.level() < 26 then
+ if cast.primalStrike() then ui.debug("Casting Primal Strike [Single]") return true end
+ end
+ -- * Flame Shock
+ -- flame_shock,if=buff.unleash_flame.up&!ticking
+ if cast.able.flameShock(units.dyn5) and buff.unleashFlame.exists() and not debuff.flameShock.exists(units.dyn5) then
+ if cast.flameShock(units.dyn5) then ui.debug("Casting Flame Shock - Unleash Flame [Single]") return true end
+ end
+ -- * Lava Lash
+ -- lava_lash
+ if cast.able.lavaLash(units.dyn5) and debuff.flameShock.exists(units.dyn5) then
+ if cast.lavaLash(units.dyn5) then ui.debug("Casting Lava Lash [Single]") return true end
+ end
+ -- * Lightning Bolt
+ -- lightning_bolt,if=set_bonus.tier15_2pc_melee=1&buff.maelstrom_weapon.react>=4&!buff.ascendance.up
+ if cast.able.lightningBolt() and equiped.tier(15) >= 2 and buff.maelstromWeapon.stack() >= 4 and not buff.ascendance.exists() then
+ if cast.lightningBolt() then
+ var.fillerLightningBolt = false
+ ui.debug("Casting Lightning Bolt - T15 2pc [Single]")
+ return true
+ end
+ end
+ -- * Flame Shock
+ -- flame_shock,if=(buff.unleash_flame.up&(dot.flame_shock.remains<10|action.flame_shock.tick_damage>dot.flame_shock.tick_dmg))|!ticking
+ if cast.able.flameShock(units.dyn5) and ((buff.unleashFlame.exists()
+ and (debuff.flameShock.remains(units.dyn5) < 10 --[[or cast.flameShock.tickDamage() > debuff.flameShock.tickDamage(units.dyn25)]]))
+ or not debuff.flameShock.exists(units.dyn5))
+ then
+ if cast.flameShock(units.dyn5) then ui.debug("Casting Flame Shock [Single]") return true end
+ end
+ -- * Unleash Elements
+ -- unleash_elements
+ if cast.able.unleashElements() then
+ if cast.unleashElements() then ui.debug("Casting Unleash Elements [Single]") return true end
+ end
+ -- * Frost Shock
+ -- frost_shock,if=glyph.frost_shock.enabled&set_bonus.tier14_4pc_melee=0
+ if cast.able.frostShock() and glyph.frostShock.enabled() and equiped.tier(14) < 4 then
+ if cast.frostShock() then ui.debug("Casting Frost Shock - Glyph [Single]") return true end
+ end
+ -- * Lightning Bolt
+ -- lightning_bolt,if=buff.maelstrom_weapon.react>=3&!buff.ascendance.up
+ if cast.able.lightningBolt() and buff.maelstromWeapon.stack() >= 3 and not buff.ascendance.exists() then
+ if cast.lightningBolt() then
+ var.fillerLightningBolt = false
+ ui.debug("Casting Lightning Bolt - 3+ Stack Maelstrom [Single]")
+ return true
+ end
+ end
+ -- * Ancestral Swiftness
+ -- ancestral_swiftness,if=talent.ancestral_swiftness.enabled&buff.maelstrom_weapon.react<2
+ if ui.alwaysCdNever("Ancestral Swiftness") and cast.able.ancestralSwiftness() and talent.ancestralSwiftness and buff.maelstromWeapon.stack() < 2 then
+ if cast.ancestralSwiftness() then ui.debug("Casting Ancestral Swiftness [Single]") return true end
+ end
+ -- * Lightning Bolt
+ -- lightning_bolt,if=buff.ancestral_swiftness.up
+ if cast.able.lightningBolt() and buff.ancestralSwiftness.exists() then
+ if cast.lightningBolt() then
+ var.fillerLightningBolt = false
+ ui.debug("Casting Lightning Bolt - Ancestral Swiftness [Single]")
+ return true
+ end
+ end
+ -- * Earth Shock
+ -- earth_shock,if=(!glyph.frost_shock.enabled|set_bonus.tier14_4pc_melee=1)
+ if cast.able.earthShock() and (not glyph.frostShock.enabled() or equiped.tier(14) >= 4) then
+ if cast.earthShock() then ui.debug("Casting Earth Shock [Single]") return true end
+ end
+ -- * Feral Spirit
+ -- feral_spirit
+ if ui.alwaysCdNever("Feral Spirit") and cast.able.feralSpirit() then
+ if cast.feralSpirit() then ui.debug("Casting Feral Spirit [Single]") return true end
+ end
+ -- * Earth Elemental Totem
+ -- earth_elemental_totem,if=!active
+ if ui.alwaysCdNever("Earth Elemental") and cast.able.earthElementalTotem("player") and not totem.earth.active() then
+ if cast.earthElementalTotem("player") then ui.debug("Casting Earth Elemental Totem [Single]") return true end
+ end
+ -- * Spiritwalkers Grace
+ -- spiritwalkers_grace,moving=1
+ if ui.alwaysCdNever("Spiritwalkers Grace") and cast.able.spiritwalkersGrace() and unit.moving() then
+ if cast.spiritwalkersGrace() then ui.debug("Casting Spiritwalkers Grace [Single]") return true end
+ end
+ -- * Lightning Bolt
+ -- lightning_bolt,if=buff.maelstrom_weapon.react>1&!buff.ascendance.up
+ if cast.able.lightningBolt() and buff.maelstromWeapon.stack() > 1 and not buff.ascendance.exists() then
+ if cast.lightningBolt() then
+ var.fillerLightningBolt = false
+ ui.debug("Casting Lightning Bolt - 2+ Stack Maelstrom [Single]")
+ return true
+ end
+ end
+ -- * Filler Lightning Bolt
+ if cast.able.lightningBolt() and canCastFillerLightning(false) then
+ if cast.lightningBolt() then
+ var.fillerLightningBolt = true
+ ui.debug("Casting Lightning Bolt - Filler [Single]")
+ return true
+ end
+ end
+ br.debug.cpu:updateDebug(startTime,"rotation.profile.single")
+end -- End Action List - Single
+
+-- Action List - PreCombat
+actionList.PreCombat = function()
+ local startTime = br._G.debugprofilestop()
+ if not unit.inCombat() and not (unit.flying() or unit.mounted() or unit.taxi()) then
+ -- * Flask
+ -- flask,type=spring_blossoms
+ -- * Food
+ -- food,type=sea_mist_rice_noodles
+ -- # Snapshot raid buffed stats before combat begins and pre-potting is done.
+ -- snapshot_stats
+ -- virmens_bite_potion
+ if ui.checked("Pre-Pull Timer") and ui.pullTimer() <= ui.value("Pre-Pull Timer") then
+ -- * Potion
+ -- potion
+ -- if ui.checked("Potion") and br.functions.item:canUseItem(142117) and unit.instance("raid") then
+ -- if feralSpiritRemain > 5 and not buff.prolongedPower.exists() then
+ -- br.functions.item:useItem(142117)
+ -- end
+ -- end
+ end -- End Pre-Pull
+ if unit.valid("target") then
+ -- * Lightning Bolt
+ if ui.checked("Lightning Bolt Out of Combat") and cast.able.lightningBolt()
+ and not unit.moving() and unit.distance("target") >= 10
+ and cast.timeSinceLast.lightningBolt() > cast.time.lightningBolt() + unit.gcd(true)
+ -- and (not ui.checked("Ghost Wolf") or unit.distance("target") <= 20 or not buff.ghostWolf.exists())
+ then
+ if cast.lightningBolt("target") then ui.debug("Casting Lightning Bolt [Precombat]") return true end
+ end
+ -- * Start Attack
+ if unit.distance("target") < 5 then
+ if cast.able.autoAttack("target") then
+ if cast.autoAttack("target") then ui.debug("Casting Auto Attack [Precombat]") return true end
+ end
+ end
+ end
+ end -- End No Combat
+ br.debug.cpu:updateDebug(startTime,"rotation.profile.precombat")
+end -- End Action List - PreCombat
+
+-- Action List - Combat
+actionList.Combat = function()
+ local startTime = br._G.debugprofilestop()
+ if (unit.inCombat() --[[or (not unit.inCombat() and unit.valid(units.dyn5))]]) and not var.profileStop
+ and unit.exists(units.dyn5) and cd.global.remain() == 0
+ then
+ -- * Cancel ranged lightning casts if enemies are in melee range
+ if (cast.current.lightningBolt() or cast.current.chainLightning()) and #enemies.yards5 > 0 then
+ if cast.current.lightningBolt() then
+ if cast.cancel.lightningBolt() then
+ ui.debug("Canceled Lightning Bolt - Enemy in melee range")
+ return true
+ end
+ elseif cast.current.chainLightning() then
+ if cast.cancel.chainLightning() then
+ ui.debug("Canceled Chain Lightning - Enemy in melee range")
+ return true
+ end
+ end
+ end
+ -- * Call Action List - Interrupt
+ -- wind_shear
+ if actionList.Interrupt() then return true end
+ -- * Heroism / Bloodlust
+ -- # Bloodlust casting behavior mirrors the simulator settings for proxy bloodlust. See options 'bloodlust_percent', and 'bloodlust_time'.
+ -- bloodlust,if=target.health.pct<25|time>0.500
+ -- * Start Attack
+ -- auto_attack
+ if cast.able.autoAttack(units.dyn5) then --and #enemies.yards5 > 0 then
+ if cast.autoAttack(units.dyn5) then
+ ui.debug("Casting Auto Attack [Combat]")
+ return true
+ end
+ end
+ -- * Ranged Lightning when not in melee range
+ if unit.valid("target") and #enemies.yards5 == 0 and unit.distance("target") > 5 and unit.distance("target") <= 40 then
+ -- Chain Lightning for multiple targets
+ if cast.able.chainLightning() and #enemies.yards8t >= 2 then
+ if cast.chainLightning() then
+ ui.debug("Casting Chain Lightning - Out of Melee [Combat]")
+ return true
+ end
+ end
+ -- Lightning Bolt for single target
+ if cast.able.lightningBolt() then
+ if cast.lightningBolt() then
+ ui.debug("Casting Lightning Bolt - Out of Melee [Combat]")
+ return true
+ end
+ end
+ end
+ -- * Use Items - Hands
+ -- use_item,name=grips_of_the_witch_doctor
+ -- * Stormlash Totem
+ -- # Link Stormlash totem cast to early Bloodlust, and ensure that only one Stormlash is used at a time.
+ -- stormlash_totem,if=!active&!buff.stormlash.up&(buff.bloodlust.up|time>=60)
+ if ui.alwaysCdNever("Stormlash Totem") and cast.able.stormlashTotem("player")
+ and not totem.air.stormlash.exists() and not buff.stormlash.exists()
+ and (buff.bloodlust.exists() or unit.combatTime() >= 60)
+ then
+ if cast.stormlashTotem("player") then
+ ui.debug("Casting Stormlash Totem")
+ return true
+ end
+ end
+ -- * In-Combat Potion
+ -- # In-combat potion is linked to Primal or Greater Fire Elemental Totem, after the first 60 seconds of combat.
+ -- virmens_bite_potion,if=time>60&(pet.primal_fire_elemental.active|pet.greater_fire_elemental.active|target.time_to_die<=60)
+ -- * Racials
+ -- blood_fury
+ -- arcane_torrent
+ -- berserking
+ if ui.alwaysCdNever("Racial") and cast.able.racial()
+ and (unit.race() == "Orc" or unit.race() == "BloodElf" or unit.race() == "Troll")
+ then
+ if cast.racial() then ui.debug("Casting Racial") return true end
+ end
+ -- * Elemental Mastery
+ -- elemental_mastery,if=talent.elemental_mastery.enabled&(talent.primal_elementalist.enabled&glyph.fire_elemental_totem.enabled&(cooldown.fire_elemental_totem.remains=0|cooldown.fire_elemental_totem.remains>=80))
+ if ui.alwaysCdNever("Elemental Mastery") and cast.able.elementalMastery() and talent.elementalMastery
+ and (talent.primalElementalist and glyph.fireElementalTotem.exists()
+ and (cd.fireElementalTotem.remain() == 0 or cd.fireElementalTotem.remain() >= 80))
+ then
+ if cast.elementalMastery() then
+ ui.debug("Casting Elemental Mastery")
+ return true
+ end
+ end
+ -- elemental_mastery,if=talent.elemental_mastery.enabled&(talent.primal_elementalist.enabled&!glyph.fire_elemental_totem.enabled&(cooldown.fire_elemental_totem.remains=0|cooldown.fire_elemental_totem.remains>=50))
+ if ui.alwaysCdNever("Elemental Mastery") and cast.able.elementalMastery() and talent.elementalMastery
+ and (talent.primalElementalist and not glyph.fireElementalTotem.enabled()
+ and (cd.fireElementalTotem.remain() == 0 or cd.fireElementalTotem.remain() >= 50))
+ then
+ if cast.elementalMastery() then
+ ui.debug("Casting Elemental Mastery")
+ return true
+ end
+ end
+ -- elemental_mastery,if=talent.elemental_mastery.enabled&!talent.primal_elementalist.enabled
+ if ui.alwaysCdNever("Elemental Mastery") and cast.able.elementalMastery() and talent.elementalMastery
+ and not talent.primalElementalist
+ then
+ if cast.elementalMastery() then
+ ui.debug("Casting Elemental Mastery")
+ return true
+ end
+ end
+ -- * Fire Elemental Totem
+ -- fire_elemental_totem,if=!active
+ if ui.alwaysCdNever("Fire Elemental") and cast.able.fireElementalTotem("player") and not totem.fireElemental.exists() then
+ if cast.fireElementalTotem("player") then
+ ui.debug("Casting Fire Elemental Totem")
+ return true
+ end
+ end
+ -- * Ascendance
+ -- ascendance,if=cooldown.strike.remains>=3
+ if ui.alwaysCdNever("Ascendance") and cast.able.ascendance() and cd.stormstrike.remain() >= 3 then
+ if cast.ascendance() then
+ ui.debug("Casting Ascendance")
+ return true
+ end
+ end
+ -- * Lifeblood
+ -- lifeblood,if=(glyph.fire_elemental_totem.enabled&(pet.primal_fire_elemental.active|pet.greater_fire_elemental.active))|!glyph.fire_elemental_totem.enabled
+ -- * Call Action List - Single
+ -- # If only one enemy, priority follows the 'single' action list.
+ -- run_action_list,name=single,if=active_enemies=1
+ if ui.useST(8,2) then
+ if actionList.Single() then return true end
+ end
+ -- * Call Action List - AOE
+ -- # On multiple enemies, the priority follows the 'aoe' action list.
+ -- run_action_list,name=aoe,if=active_enemies>1
+ if ui.useAOE(8,2) then
+ if actionList.AOE() then return true end
+ end
+ end
+ br.debug.cpu:updateDebug(startTime,"rotation.profile.combat")
+end -- End Action List - Combat
+
+----------------
+--- ROTATION ---
+----------------
+local function runRotation()
+ --------------
+ --- Locals ---
+ --------------
+ -- BR API
+ buff = br.player.buff
+ cast = br.player.cast
+ cd = br.player.cd
+ debuff = br.player.debuff
+ enemies = br.player.enemies
+ equiped = br.player.equiped
+ glyph = br.player.glyph
+ mana = br.player.power.mana
+ spell = br.player.spell
+ talent = br.player.talent
+ totem = br.player.totem
+ ui = br.player.ui
+ unit = br.player.unit
+ units = br.player.units
+ var = br.player.variables
+
+ -- Dynamic Units
+ units.get(5)
+
+ -- Enemies Lists
+ enemies.get(5)
+ enemies.get(8)
+ enemies.get(8, "target")
+ enemies.get(10)
+ enemies.get(20)
+ enemies.get(25,"player",true,false)
+ enemies.get(30)
+
+ -- Initialize filler tracking variables
+ if var.fillerLightningBolt == nil then var.fillerLightningBolt = false end
+ if var.fillerChainLightning == nil then var.fillerChainLightning = false end
+
+ -- Cancel filler casts if a priority ability becomes available (check BEFORE clearing flags)
+ if unit.inCombat() and (var.fillerLightningBolt or var.fillerChainLightning) then
+ local isCasting = cast.current.lightningBolt() or cast.current.chainLightning()
+ if isCasting then
+ local timeToNext = getTimeToNextAbility()
+ -- Cancel if a priority ability is ready NOW or will be ready very soon
+ if timeToNext <= 0.1 then
+ if cancelFillerLightning() then
+ ui.debug("Canceled filler cast - priority ability ready (timeToNext: "..tostring(timeToNext)..")")
+ end
+ end
+ else
+ -- Not casting anymore, clear the filler flags
+ var.fillerLightningBolt = false
+ var.fillerChainLightning = false
+ end
+ end
+
+
+ var.healUnit = ui.value("Heal Target") == 1 and unit.lowest(40) or "player"
+ var.healHP = unit.hp(var.healUnit)
+ var.unitsNeedingHealing = 0
+ if ui.checked("Use HST While Solo") and br.functions.unit:getHP("player") <= ui.value("Healing Stream Totem") then
+ var.unitsNeedingHealing = var.unitsNeedingHealing + 1
+ end
+ if #br.engines.healingEngine.friend > 1 then
+ for i = 1, #br.engines.healingEngine.friend do
+ local thisFriend = br.engines.healingEngine.friend[i].unit
+ local thisDistance = unit.distance(thisFriend)
+ if not unit.isUnit(thisFriend,"player") and thisDistance < 40 and unit.hp(thisFriend) <= ui.value("Healing Stream Totem") then
+ var.unitsNeedingHealing = var.unitsNeedingHealing + 1
+ end
+ end
+ end
+
+ if var.profileStop == nil then var.profileStop = false end
+
+ ---------------------
+ --- Begin Profile ---
+ ---------------------
+ -- * Profile Stop | Pause
+ if not unit.inCombat() and not unit.exists("target") and var.profileStop then
+ var.profileStop = false
+ elseif (unit.inCombat() and var.profileStop) or ui.pause() then
+ return true
+ else
+ -----------------------
+ --- Extras Rotation ---
+ -----------------------
+ if actionList.Extras() then return true end
+ --------------------------
+ --- Defensive Rotation ---
+ --------------------------
+ if actionList.Defensive() then return true end
+ -------------------
+ --- No Rotation ---
+ -------------------
+ if ui.mode.rotation == 4 then return true end
+ ---------------------------
+ --- Pre-Combat Rotation ---
+ ---------------------------
+ if actionList.PreCombat() then return true end
+ --------------------------
+ --- In Combat Rotation ---
+ --------------------------
+ if actionList.Combat() then return true end
+ end --End Rotation Logic
+end -- End runRotation
+local id = 263
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id],{
+ name = rotationName,
+ toggles = createToggles,
+ options = createOptions,
+ run = runRotation,
+})
diff --git a/Rotations/Shaman/Initial/InitialShaman.lua b/Rotations/Shaman/Initial/InitialShaman.lua
index 310fcb20a7..069607763c 100644
--- a/Rotations/Shaman/Initial/InitialShaman.lua
+++ b/Rotations/Shaman/Initial/InitialShaman.lua
@@ -16,12 +16,6 @@ local function createToggles()
[2] = { mode = "Off", value = 2, overlay = "Defensive Disabled", tip = "No Defensives will be used.", highlight = 0, icon = br.player.spells.healingSurge }
};
br.ui:createToggle(DefensiveModes, "Defensive", 2, 0)
- -- Ghost Wolf Button
- local GhostWolfModes = {
- [1] = { mode = "Moving", value = 1, overlay = "Moving Enabled", tip = "Will Ghost Wolf when movement detected", highlight = 1, icon = br.player.spells.ghostWolf },
- [2] = { mode = "Hold", value = 2, overlay = "Hold Enabled", tip = "Will Ghost Wolf when key is held down", highlight = 0, icon = br.player.spells.ghostWolf },
- };
- br.ui:createToggle(GhostWolfModes, "GhostWolf", 3, 0)
end
---------------
@@ -36,16 +30,7 @@ local function createOptions()
--- GENERAL OPTIONS ---
-----------------------
section = br.ui:createSection(br.ui.window.profile, "General")
- -- Basic Trinket Module
- br.player.module.BasicTrinkets(nil, section)
- -- Ghost Wolf
- br.ui:createCheckbox(section, "Auto Ghost Wolf",
- "|cff0070deCheck this to automatically control GW transformation based on toggle bar setting.")
- br.ui:createSpinnerWithout(section, "Ghost Wolf Shift Delay", 2, 0, 5, 1,
- "|cff0070deSet to desired time to wait before shifting into Ghost Wolf.")
- br.ui:createDropdownWithout(section, "Ghost Wolf Key", br.dropOptions.Toggle, 6,
- "|cff0070deSet key to hold down for Ghost Wolf")
- -- FLametongue Weapon
+ -- Flametongue Weapon
br.ui:createCheckbox(section, "Flametongue Weapon", "|cff0070deCheck this to keep flametongue weapon enchant up.")
-- Lightning Shield
br.ui:createCheckbox(section, "Lightning Shield", "|cff0070deCheck this to keep lightning shield up.")
@@ -54,8 +39,6 @@ local function createOptions()
--- DEFENSIVE OPTIONS ---
-------------------------
section = br.ui:createSection(br.ui.window.profile, "Defensive")
- -- Basic Healing Module
- br.player.module.BasicHealing(section)
-- Healing Surge
br.ui:createSpinner(section, "Healing Surge", 50, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
-- OOC Healing
@@ -67,11 +50,9 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
--Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
- -- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -91,6 +72,7 @@ local cd
local debuff
local enemies
local module
+local spell
local ui
local unit
local units
@@ -100,46 +82,44 @@ local actionList = {}
-----------------
--- Functions ---
-----------------
-local function ghostWolf()
- -- Ghost Wolf
- local moveTimer = unit.movingTime()
- if not (unit.mounted() or unit.flying()) and ui.checked("Auto Ghost Wolf") then
- if ui.mode.ghostWolf == 1 then
- if cast.able.ghostWolf() and ((#enemies.yards20 == 0 and not unit.inCombat()) or (#enemies.yards10 == 0 and unit.inCombat()))
- and unit.moving() and moveTimer > ui.value("Ghost Wolf Shift Delay") and not buff.ghostWolf.exists()
- then
- if cast.ghostWolf("player") then ui.debug("Casting Ghost Wolf [Moving]") end
- end
- elseif ui.mode.ghostWolf == 2 then
- if cast.able.ghostWolf() and not buff.ghostWolf.exists() and unit.moving() then
- if ui.toggle("Ghost Wolf Key") then
- if cast.ghostWolf("player") then ui.debug("Casting Ghost Wolf [Keybind]") end
- end
- elseif buff.ghostWolf.exists() then
- if ui.toggle("Ghost Wolf Key") then return end
- end
+local function cancelLightningBolt()
+ if cast.current.lightningBolt() and cast.timeRemain() > unit.gcd(true) then
+ if cast.cancel.lightningBolt() then
+ ui.debug("Canceled Lightning Bolt Cast [Melee Range]")
+ return true
end
end
+ return false
+end
+local function canLightningBolt()
+ -- Earth Shock
+ if (not spell.earthShock.known() or cast.time.lightningBolt() > cd.earthShock.remain()) and
+ unit.distance(units.dyn25) < 25
+ then
+ return false
+ end
+ -- Primal Strike
+ if (not spell.primalStrike.known() or cast.time.lightningBolt() > cd.primalStrike.remain()) and unit.distance(units.dyn5) < 5 then
+ return false
+ end
+ return true
end
-
--------------------
--- Action Lists ---
--------------------
-- Action List - Extra
actionList.Extra = function()
- -- FLametongue Weapon
+ -- Flametongue Weapon
if ui.checked("Flametongue Weapon") and cast.able.flametongueWeapon() and not unit.weaponImbue.exists() then
if cast.flametongueWeapon("player") then
- ui.debug("Casting Flametongue Weapon")
+ ui.debug("Casting Flametongue Weapon [Extra]")
return true
end
end
- -- Ghost Wolf
- ghostWolf()
-- Lightning Shield
if ui.checked("Lightning Shield") and cast.able.lightningShield() and not buff.lightningShield.exists() then
if cast.lightningShield("player") then
- ui.debug("Casting Lightning Shield")
+ ui.debug("Casting Lightning Shield [Extra]")
return true
end
end
@@ -148,18 +128,16 @@ end -- End Action List - Extra
-- Action List - Defensive
actionList.Defensive = function()
if ui.useDefensive() then
- -- Basic Healing Module
- module.BasicHealing()
-- Healing Surge
if ui.checked("Healing Surge") and cast.able.healingSurge() and not unit.moving() then
if unit.friend("target") and unit.hp("target") <= ui.value("Healing Surge") then
if cast.healingSurge("target") then
- ui.debug("Casting Healing Surge on " .. unit.name("target"))
+ ui.debug("Casting Healing Surge on " .. unit.name("target") .. " [Defensive]")
return true
end
elseif unit.hp("player") <= ui.value("Healing Surge") then
if cast.healingSurge("player") then
- ui.debug("Casting Healing Surge on " .. unit.name("player"))
+ ui.debug("Casting Healing Surge on " .. unit.name("player") .. " [Defensive]")
return true
end
end
@@ -169,30 +147,37 @@ end -- End Action List - Defensive
-- Action List - Pre-Combat
actionList.PreCombat = function()
- if not unit.inCombat() then
- if unit.distance("target") > 5 then
- -- Lightning Bolt
- if cast.able.lightningBolt() and not unit.moving() then
- if cast.lightningBolt() then
- ui.debug("Casting Lightning Bolt [Pre-Combat] " ..
- br.round2(cast.timeSinceLast.lightningBolt(), 2) ..
- " " .. unit.gcd(true) + (select(3, br._G.GetNetStats()) / 100))
- return true
+ if not unit.inCombat() and not (unit.flying() or unit.mounted()) then
+ if unit.valid("target") and unit.exists("target") then
+ if unit.distance("target") > 5 then
+ -- Lightning Bolt
+ if cast.able.lightningBolt("target") and var.useLightningBolt then
+ if cast.lightningBolt("target") then
+ ui.debug("Casting Lightning Bolt [Precombat]")
+ return true
+ end
end
- end
- -- Flame Shock
- if cast.able.flameShock() then
- if cast.flameShock() then
- ui.debug("Casting Flame Shock [Pre-Combat]")
- return true
+ -- Earth Shock
+ if cast.able.earthShock("target") then
+ if cast.earthShock("target") then
+ ui.debug("Casting Earth Shock [Precombat]")
+ return true
+ end
end
- end
- else
- -- Start Attack
- if cast.able.autoAttack("target") then
- if cast.autoAttack("target") then
- ui.debug("Casting Auto Attack [Precombat]")
- return true
+ else
+ -- Primal Strike
+ if cast.able.primalStrike("target") then
+ if cast.primalStrike("target") then
+ ui.debug("Casting Primal Strike [Precombat]")
+ return true
+ end
+ end
+ -- Start Attack
+ if cast.able.autoAttack("target") then
+ if cast.autoAttack("target") then
+ ui.debug("Casting Auto Attack [Precombat]")
+ return true
+ end
end
end
end
@@ -201,34 +186,36 @@ end -- End Action List - PreCombat
-- Action List - Combat
actionList.Combat = function()
- -- Start Attack
- if cast.able.autoAttack("target") then
- if cast.autoAttack("target") then
- ui.debug("Casting Auto Attack")
- return true
+ if (unit.inCombat() or (not unit.inCombat() and unit.valid(units.dyn5))) and not var.profileStop
+ and unit.exists(units.dyn5) and cd.global.remain() == 0
+ then
+ -- Start Attack
+ if cast.able.autoAttack("target") then
+ if cast.autoAttack("target") then
+ ui.debug("Casting Auto Attack [Combat]")
+ return true
+ end
end
- end
- -- Basic Trinkets Module
- module.BasicTrinkets()
- -- Flame Shock
- if cast.able.flameShock() and not debuff.flameShock.exists(units.dyn40) then
- if cast.flameShock() then
- ui.debug("Casting Flame Shock")
- return true
+ -- Earth Shock
+ if cast.able.earthShock() then
+ if cast.earthShock() then
+ ui.debug("Casting Earth Shock [Combat]")
+ return true
+ end
end
- end
- -- Primal Strike
- if cast.able.primalStrike() then
- if cast.primalStrike() then
- ui.debug("Casting Primal Strike")
- return true
+ -- Primal Strike
+ if cast.able.primalStrike() then
+ if cast.primalStrike() then
+ ui.debug("Casting Primal Strike [Combat]")
+ return true
+ end
end
- end
- -- Lightning Bolt
- if cast.able.lightningBolt() and not unit.moving() and (unit.distance("target") > 5 or unit.level() < 2) then
- if cast.lightningBolt() then
- ui.debug("Casting Lightning Bolt")
- return true
+ -- Lightning Bolt
+ if cast.able.lightningBolt() and var.useLightningBolt then
+ if cast.lightningBolt() then
+ ui.debug("Casting Lightning Bolt [Combat]")
+ return true
+ end
end
end
end -- End Action List - Combat
@@ -247,6 +234,7 @@ local function runRotation()
debuff = br.player.debuff
enemies = br.player.enemies
module = br.player.module
+ spell = br.player.spell
ui = br.player.ui
unit = br.player.unit
units = br.player.units
@@ -257,6 +245,7 @@ local function runRotation()
ui.mode.rotation == 2
-- Units
units.get(5)
+ units.get(25)
units.get(40)
-- Enemies
enemies.get(5)
@@ -264,19 +253,18 @@ local function runRotation()
enemies.get(20)
enemies.get(40)
-
- -- Cancel Lightning Bolt in Melee
- if unit.distance("target") < 5 and cast.current.lightningBolt() and unit.level() > 1 then
- if cast.cancel.lightningBolt() then ui.debug("Canceled Lightning Bolt Cast [Melee Range]") end
+ var.useLightningBolt = canLightningBolt()
+ if not var.useLightningBolt then
+ cancelLightningBolt()
end
---------------------
--- Begin Profile ---
---------------------
- -- Profile Stop | Pause
+ -- * Profile Stop | Pause
if not unit.inCombat() and not unit.exists("target") and var.profileStop then
var.profileStop = false
- elseif var.haltProfile then
+ elseif (unit.inCombat() and var.profileStop) or ui.pause() then
return true
else
-----------------------
@@ -287,22 +275,23 @@ local function runRotation()
--- Defensive ---
-----------------
if actionList.Defensive() then return true end
- ------------------
- --- Pre-Combat ---
- ------------------
- if actionList.PreCombat() then return true end
- -----------------------------
- --- In Combat - Rotations ---
- -----------------------------
- if unit.inCombat() and unit.valid("target") and cd.global.remain() == 0 then
+ if ui.mode.rotation ~= 2 then
+ ------------------
+ --- Pre-Combat ---
+ ------------------
+ if actionList.PreCombat() then return true end
+ -----------------------------
+ --- In Combat - Rotations ---
+ -----------------------------
if actionList.Combat() then return true end
end -- End In Combat Rotation
end -- Pause
return true
end -- End runRotation
local id = 1444
-if br.rotations[id] == nil then br.rotations[id] = {} end
-br._G.tinsert(br.rotations[id], {
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Shaman/Shaman/Shaman-TBC.lua b/Rotations/Shaman/Shaman/Shaman-TBC.lua
new file mode 100644
index 0000000000..43d4c241b7
--- /dev/null
+++ b/Rotations/Shaman/Shaman/Shaman-TBC.lua
@@ -0,0 +1,458 @@
+local rotationName = "CuteOne" -- Change to name of profile listed in options drop down
+
+---------------
+--- Toggles ---
+---------------
+local function createToggles() -- Define custom toggles
+ -- Rotation Button
+ local RotationModes = {
+ [1] = { mode = "Auto", value = 1, overlay = "Automatic Rotation", tip = "Swaps between Single and Multiple based on number of #enemies.yards8 in range.", highlight = 1, icon = br.player.spells.chainLightning },
+ [2] = { mode = "Multi", value = 2, overlay = "Multiple Target Rotation", tip = "Forces Multiple Target Rotation.", highlight = 0, icon = br.player.spells.chainLightning },
+ [3] = { mode = "Single", value = 3, overlay = "Single Target Rotation", tip = "Forces Single Target Rotation.", highlight = 0, icon = br.player.spells.lightningBolt },
+ [4] = { mode = "Off", value = 4, overlay = "DPS Rotation Disabled", tip = "Disable DPS Rotation", highlight = 0, icon = br.player.spells.healingWave }
+ };
+ br.ui:createToggle(RotationModes, "Rotation", 1, 0)
+ -- Cooldown Button
+ local CooldownModes = {
+ [1] = { mode = "Auto", value = 1, overlay = "Cooldowns Automated", tip = "Automatic Cooldowns - Boss Detection.", highlight = 1, icon = br.player.spells.heroism },
+ [2] = { mode = "On", value = 2, overlay = "Cooldowns Enabled", tip = "Cooldowns used regardless of target.", highlight = 0, icon = br.player.spells.heroism },
+ [3] = { mode = "Off", value = 3, overlay = "Cooldowns Disabled", tip = "No Cooldowns will be used.", highlight = 0, icon = br.player.spells.heroism }
+ };
+ br.ui:createToggle(CooldownModes, "Cooldown", 2, 0)
+ -- Defensive Button
+ local DefensiveModes = {
+ [1] = { mode = "On", value = 1, overlay = "Defensive Enabled", tip = "Includes Defensive Cooldowns.", highlight = 1, icon = br.player.spells.healingWave },
+ [2] = { mode = "Off", value = 2, overlay = "Defensive Disabled", tip = "No Defensives will be used.", highlight = 0, icon = br.player.spells.healingWave }
+ };
+ br.ui:createToggle(DefensiveModes, "Defensive", 3, 0)
+ -- Interrupt Button
+ local InterruptModes = {
+ [1] = { mode = "On", value = 1, overlay = "Interrupts Enabled", tip = "Includes Basic Interrupts.", highlight = 1, icon = br.player.spells.earthShock },
+ [2] = { mode = "Off", value = 2, overlay = "Interrupts Disabled", tip = "No Interrupts will be used.", highlight = 0, icon = br.player.spells.earthShock }
+ };
+ br.ui:createToggle(InterruptModes, "Interrupt", 4, 0)
+end
+---------------
+--- OPTIONS ---
+---------------
+local function createOptions()
+ local optionTable
+
+ local function rotationOptions()
+ local section
+ -----------------------
+ --- GENERAL OPTIONS ---
+ -----------------------
+ section = br.ui:createSection(br.ui.window.profile, "General")
+ -- Purge
+ br.ui:createCheckbox(section,"Purge")
+ br.ui:checkSectionState(section)
+ --------------------------
+ --- TOTEM MANAGEMENT ---
+ --------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Totem Management")
+ -- Earth Totems
+ br.ui:createDropdownWithout(section, "Earth Totem", {"Stoneskin Totem", "Strength of Earth Totem", "None"}, 1)
+ br.ui:checkSectionState(section)
+ ---------------------
+ --- BUFF OPTIONS ---
+ ---------------------
+ section = br.ui:createSection(br.ui.window.profile, "Buffs")
+ -- Shield's Up
+ br.ui:createDropdownWithout(section, "Shield's Up", {"Lightning Shield", "Water Shield", "None"}, 1)
+ -- Weapon's Online
+ br.ui:createDropdownWithout(section, "MH Weapon's Online", {"Rockbiter Weapon", "Flametongue Weapon", "Frostbrand Weapon", "None"}, 1)
+ br.ui:createDropdownWithout(section, "OH Weapon's Online", {"Rockbiter Weapon", "Flametongue Weapon", "Frostbrand Weapon", "None"}, 2)
+ -- Water Shield Swap At
+ br.ui:createSpinnerWithout(section, "Water Shield Swap At", 30, 0, 100, 5, "|cffFFFFFFMana Percent to Swap to Water Shield")
+ br.ui:checkSectionState(section)
+ -------------------------
+ --- DEFENSIVE OPTIONS ---
+ -------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Defensive")
+ -- Ancestral Spirit
+ br.ui:createDropdown(section, "Ancestral Spirit", {"|cffFFFF00Selected Target","|cffFF0000Mouseover Target"}, 1, "|ccfFFFFFFTarget to Cast On")
+ -- Healing Wave
+ br.ui:createSpinner(section, "Healing Wave", 50, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ -- Stoneclaw Totem
+ br.ui:createSpinner(section, "Stoneclaw Totem Defensive", 45, 0, 100, 5,
+ "|cffFFFFFFHealth Percent to Cast At (requires Earth Totem = Stoneclaw Totem and no tank nearby)")
+ -- OOC Healing
+ br.ui:createCheckbox(section, "OOC Healing",
+ "|cff15FF00Enables|cffFFFFFF/|cffD60000Disables |cffFFFFFFout of combat healing|cffFFBB00.")
+ br.ui:checkSectionState(section)
+ -------------------------
+ --- Interrupt Options ---
+ -------------------------
+ section = br.ui:createSection(br.ui.window.profile, "Interrupts")
+ -- Interrupt Percentage
+ br.ui:createSpinnerWithout(section, "Interrupt At", 0, 0, 95, 5,
+ "|cffFFFFFFCast Percent to Cast At (0 is random)")
+ br.ui:checkSectionState(section)
+ ----------------------
+ --- TOGGLE OPTIONS ---
+ ----------------------
+ section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
+ -- Single/Multi Toggle
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
+ -- Cooldown Key Toggle
+ br.ui:createDropdownWithout(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 3)
+ -- Defensive Key Toggle
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
+ -- Interrupt Key Toggle
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
+ br.ui:checkSectionState(section)
+ end
+ optionTable = { {
+ [1] = "Rotation Options",
+ [2] = rotationOptions,
+ } }
+ return optionTable
+end
+
+--------------
+--- Locals ---
+--------------
+-- BR API Locals - Many of these are located from System/API, this is a sample of commonly used ones but no all inclusive
+local buff
+local cast
+local cd
+local debuff
+local enemies
+local mana
+local module
+local ui
+local unit
+local units
+local spell
+local totem
+local var = {}
+-- General Locals - Common Non-BR API Locals used in profiles
+local haltProfile
+local profileStop
+-- Profile Specific Locals - Any custom to profile locals
+local actionList = {}
+
+
+-----------------
+--- Functions --- -- List all profile specific custom functions here
+-----------------
+
+
+--------------------
+--- Action Lists ---
+--------------------
+
+-- Action List - Extra
+actionList.Extra = function()
+ -- * Lightning Shield
+ if ui.value("Shield's Up") == 1 and cast.able.lightningShield() and not buff.lightningShield.exists()
+ and (mana.percent() >= math.min(100, ui.value("Water Shield Swap At") * 2)
+ or not ui.value("Shield's Up") == 2 or not spell.waterShield.known())
+ then
+ if cast.lightningShield() then ui.debug("Casting Lightning Shield [Extra]") return true end
+ end
+ -- * Rockbiter Weapon
+ if ui.value("MH Weapon's Online") == 1 and cast.able.rockbiterWeapon()
+ and not (unit.weaponImbue.exists(29) or unit.weaponImbue.exists(6))
+ then
+ if cast.rockbiterWeapon("player") then ui.debug("Casting Rockbiter Weapon - Main [Extra]") return true end
+ end
+ if ui.value("OH Weapon's Online") == 1 and cast.able.rockbiterWeapon()
+ and not (unit.weaponImbue.exists(29,true) or unit.weaponImbue.exists(6,true))
+ then
+ if cast.rockbiterWeapon("player") then ui.debug("Casting Rockbiter Weapon - Off [Extra]") return true end
+ end
+ -- * Purge
+ if ui.checked("Purge") and cast.able.purge() and cast.dispel.purge("target") and not unit.isBoss() and unit.exists("target") then
+ if cast.purge() then ui.debug("Casting Purge [Extra]") return true end
+ end
+end -- End Action List - Extra
+
+-- Action List - Defensive
+actionList.Defensive = function()
+ if ui.useDefensive() then
+ -- * Ancestral Spirit
+ if ui.checked("Ancestral Spirit") and cast.timeSinceLast.ancestralSpirit() > 5 then
+ if ui.value("Ancestral Spirit")==1 and cast.able.ancestralSpirit("target","dead") and unit.player("target") then
+ if cast.ancestralSpirit("target","dead") then ui.debug("Casting Ancestral Spirit - Target [Defensive]") return true end
+ end
+ if ui.value("Ancestral Spirit")==2 and cast.able.ancestralSpirit("mouseover","dead") and unit.player("mouseover") then
+ if cast.ancestralSpirit("mouseover","dead") then ui.debug("Casting Ancestral Spirit - Mouseover [Defensive]") return true end
+ end
+ end
+ -- * Healing Wave
+ if ui.checked("Healing Wave") and cast.able.healingWave() and not unit.moving() then
+ if unit.friend("target") and unit.hp("target") <= ui.value("Healing Wave") then
+ if cast.healingWave("target") then
+ ui.debug("Casting Healing Wave on " .. unit.name("target") .. " [Defensive]")
+ return true
+ end
+ elseif unit.hp("player") <= ui.value("Healing Wave") then
+ if cast.healingWave("player") then
+ ui.debug("Casting Healing Wave on " .. unit.name("player") .. " [Defensive]")
+ return true
+ end
+ end
+ end
+ end
+end -- End Action List - Defensive
+
+-- * Action List - Interrupts
+actionList.Interrupts = function()
+ if ui.useInterrupt() and ui.delay("Interrupts", unit.gcd(true)) then
+ local thisUnit
+ -- * Earth Shock (20-yard instant interrupt, locks spell school for 2 sec)
+ for i = 1, #enemies.yards20 do
+ thisUnit = enemies.yards20[i]
+ if unit.interruptable(thisUnit, ui.value("Interrupt At")) then
+ if cast.able.earthShock(thisUnit) then
+ if cast.earthShock(thisUnit) then
+ ui.debug("Casting Earth Shock on " .. unit.name(thisUnit))
+ return true
+ end
+ end
+ end
+ end
+ end -- End useInterrupts check
+end -- End Action List - Interrupts
+
+-- Action List - Cooldowns
+actionList.Cooldowns = function()
+ if unit.distance("target") < 5 then
+ -- * Module- Basic Trinkets
+ -- use_items
+ module.BasicTrinkets()
+ end
+end -- End Action List - Cooldowns
+
+-- Action List - Totem Management
+actionList.TotemManagement = function()
+ -- * Fire Totems
+ -- Fire Nova Totem
+ if cast.able.fireNovaTotem("player","aoe",1,10) and ui.useAOE(2,10)
+ and (unit.inCombat() or (unit.valid("target") and unit.distance("target") < 10))
+ and not totem.fire.fireNova.exists() and unit.standingTime() > 1
+ then
+ if cast.fireNovaTotem("player","aoe",1,10) then ui.debug("Casting Fire Nova Totem [AOE]") return true end
+ end
+ -- * Earth Totems
+ -- Stoneclaw Totem
+ if ui.checked("Stoneclaw Totem Defensive") and ui.value("Earth Totem") == 2 and cast.able.stoneclawTotem()
+ and unit.inCombat() and unit.hp("player") <= ui.value("Stoneclaw Totem Defensive") and unit.standingTime() > 1
+ then
+ local tankInRange = unit.isTankInRange()
+ local stoneclawExists = totem.earth.stoneclaw and totem.earth.stoneclaw.exists and totem.earth.stoneclaw.exists()
+ local stoneclawDistance = (totem.earth.stoneclaw and totem.earth.stoneclaw.distance and totem.earth.stoneclaw.distance()) or 99
+ if not tankInRange and enemies.yards15 > 0 and (not stoneclawExists or stoneclawDistance > 15) then
+ if cast.stoneclawTotem() then
+ ui.debug("Casting Stoneclaw Totem [Defensive]")
+ return true
+ end
+ end
+ end
+ -- Stoneskin Totem
+ if ui.value("Earth Totem") == 1 and cast.able.stoneskinTotem()
+ and (unit.inCombat() or (unit.valid("target") and unit.distance("target") < 20))
+ and (not totem.earth.stoneskin.exists() or totem.earth.stoneskin.distance() > 20) and unit.standingTime() > 1
+ then
+ if cast.stoneskinTotem() then ui.debug("Casting Stoneskin Totem [Totem Management]") return true end
+ end
+ -- Strength of Earth Totem
+ if ui.value("Earth Totem") == 2 and cast.able.strengthOfEarthTotem()
+ and (unit.inCombat() or (unit.valid("target") and unit.distance("target") < 20))
+ and (not totem.earth.strengthOfEarth.exists() or totem.earth.strengthOfEarth.distance() > 20) and unit.standingTime() > 1
+ then
+ if cast.strengthOfEarthTotem() then ui.debug("Casting Strength of Earth Totem [Totem Management]") return true end
+ end
+end -- End Action List - Totem Management
+
+-- Action List - Pre-Combat
+actionList.PreCombat = function()
+ if not unit.inCombat() and not (unit.flying() or unit.mounted()) then
+ if unit.valid("target") and unit.exists("target") then
+ if unit.distance("target") > 5 then
+ -- Lightning Bolt
+ if cast.able.lightningBolt("target") and not unit.moving() then --and var.useLightningBolt then
+ if cast.lightningBolt("target") then
+ ui.debug("Casting Lightning Bolt [Precombat]")
+ return true
+ end
+ end
+ -- Flame Shock
+ if cast.able.flameShock("target") then
+ if cast.flameShock("target") then
+ ui.debug("Casting Flame Shock [Precombat]")
+ return true
+ end
+ end
+ -- Earth Shock
+ if cast.able.earthShock("target") then
+ if cast.earthShock("target") then
+ ui.debug("Casting Earth Shock [Precombat]")
+ return true
+ end
+ end
+ else
+ -- -- Primal Strike
+ -- if cast.able.primalStrike("target") then
+ -- if cast.primalStrike("target") then
+ -- ui.debug("Casting Primal Strike [Precombat]")
+ -- return true
+ -- end
+ -- end
+ -- Start Attack
+ if cast.able.autoAttack("target") then
+ if cast.autoAttack("target") then
+ ui.debug("Casting Auto Attack [Precombat]")
+ return true
+ end
+ end
+ end
+ end
+ end
+ -- if not unit.inCombat() and not (unit.flying() or unit.mounted()) then
+ -- if unit.valid("target") then
+ -- local thisDistance = unit.distance("target") or 99
+
+ -- if thisDistance < 5 then
+ -- -- Auto Attack
+ -- if not cast.auto.autoAttack() and unit.valid("target") and not unit.deadOrGhost("target") and unit.distance("target") < 5 then
+ -- br._G.StartAttack()
+ -- ui.debug("Casting Auto Attack [Precombat]")
+ -- return true
+ -- end
+ -- end
+ -- end
+ -- end
+end -- End Action List - PreCombat
+
+-- Action List - Combat
+actionList.Combat = function()
+ if (unit.inCombat() or (not unit.inCombat() and unit.valid(units.dyn5))) and not var.profileStop
+ and unit.exists(units.dyn5) and cd.global.remain() == 0
+ then
+ -- Start Attack
+ if cast.able.autoAttack("target") then
+ if cast.autoAttack("target") then
+ ui.debug("Casting Auto Attack [Combat]")
+ return true
+ end
+ end
+ -- Flame Shock
+ if cast.able.flameShock() and debuff.flameShock.refresh(units.dyn5) and unit.ttd(units.dyn5) > 6 then
+ if cast.flameShock() then
+ ui.debug("Casting Flame Shock [Combat]")
+ return true
+ end
+ end
+ -- Earth Shock
+ if cast.able.earthShock() then
+ if cast.earthShock() then
+ ui.debug("Casting Earth Shock [Combat]")
+ return true
+ end
+ end
+ -- -- Primal Strike
+ -- if cast.able.primalStrike() then
+ -- if cast.primalStrike() then
+ -- ui.debug("Casting Primal Strike [Combat]")
+ -- return true
+ -- end
+ -- end
+ -- Lightning Bolt
+ if cast.able.lightningBolt() and not unit.moving()
+ and (unit.distance(units.dyn5) > 10 or mana.percent() > 50)-- or unit.ttd(units.dyn5) < 3)
+ then--and var.useLightningBolt then
+ if cast.lightningBolt() then
+ ui.debug("Casting Lightning Bolt [Combat]")
+ return true
+ end
+ end
+ end -- End Combat Check
+end -- End Action list - Combat
+
+----------------
+--- ROTATION ---
+----------------
+local function runRotation()
+ ---------------------
+ --- Define Locals ---
+ ---------------------
+ -- BR API Locals
+ buff = br.player.buff
+ cast = br.player.cast
+ cd = br.player.cd
+ debuff = br.player.debuff
+ enemies = br.player.enemies
+ mana = br.player.power.mana
+ module = br.player.module
+ ui = br.player.ui
+ unit = br.player.unit
+ units = br.player.units
+ spell = br.player.spell
+ totem = br.player.totem
+ -- General Locals
+ profileStop = profileStop or false
+ haltProfile = (unit.inCombat() and profileStop) or ui.pause() or ui.mode.rotation == 4 or unit.id("target") == 156716
+
+ -- Units
+ units.get(5) -- Makes a variable called, "target"
+ -- units.get(40) -- Makes a variable called, units.dyn40
+ units.get(40, true) -- Makes a variable called, units.dyn40AOE
+ -- Enemies
+ enemies.get(5) -- Makes a varaible called, enemies.yards5
+ enemies.get(15) -- Makes a varaible called, enemies.yards15
+ enemies.get(20) -- Makes a variable called, enemies.yards20
+ -- enemies.get(20, "player", true) -- makes enemies.yards20nc
+ enemies.get(40) -- Makes a varaible called, enemies.yards40
+
+
+ if not unit.inCombat() and not unit.exists("target") then
+ if profileStop then profileStop = false end
+ end
+
+ ---------------------
+ --- Begin Profile ---
+ ---------------------
+ -- Profile Stop | Pause
+ if not unit.inCombat() and not unit.exists("target") and profileStop then
+ profileStop = false
+ elseif haltProfile then
+ return true
+ else
+ ---------------------------------
+ --- Out Of Combat - Rotations ---
+ ---------------------------------
+ -----------------------
+ --- Extras Rotation ---
+ -----------------------
+ if actionList.Extra() then return true end
+ -----------------
+ --- Defensive ---
+ -----------------
+ if actionList.Defensive() then return true end
+ -----------------------
+ --- Totem Management ---
+ -----------------------
+ if actionList.TotemManagement() then return true end
+ ------------------
+ --- Pre-Combat ---
+ ------------------
+ if actionList.PreCombat() then return true end
+ -----------------------------
+ --- In Combat - Rotations ---
+ -----------------------------
+ if actionList.Combat() then return true end
+ end -- Pause
+ return true
+end -- End runRotation
+local id = 261 -- Change to the spec id profile is for.
+local expansion = br.isBC -- Change to the expansion the profile is for.
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
+ name = rotationName,
+ toggles = createToggles,
+ options = createOptions,
+ run = runRotation,
+})
diff --git a/Rotations/Warlock/Affliction/BrewAffliction.lua b/Rotations/Warlock/Affliction/BrewAffliction.lua
index 53f08e37a1..3900f266ea 100644
--- a/Rotations/Warlock/Affliction/BrewAffliction.lua
+++ b/Rotations/Warlock/Affliction/BrewAffliction.lua
@@ -97,11 +97,11 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
--Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = {{
@@ -182,7 +182,7 @@ end
actionList.PreCombat = function()
if not unit.moving() and unit.level() >= 3
and br._G.GetTime() - br.pauseTime > 0.5
- and br.timer:useTimer("summonPet", 1) then
+ and br.debug.timer:useTimer("summonPet", 1) then
if mode.petSummon == 1 and activePet ~= pets.imp and cast.able.summonImp() and not cast.last.summonImp() then
if cast.summonImp("player") then return true end
end
@@ -324,9 +324,10 @@ local function runRotation()
end
end
local id = 265 -- Affliction Warlock
+local expansion = br.isMOP
-if br.rotations[id] == nil then br.rotations[id] = {} end
-tinsert(br.rotations[id],{
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+tinsert(br.loader.rotations[id],{
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Warlock/Demonology/BrewDemon.lua b/Rotations/Warlock/Demonology/BrewDemon.lua
index 23a7d3daa1..9e5eff7a56 100644
--- a/Rotations/Warlock/Demonology/BrewDemon.lua
+++ b/Rotations/Warlock/Demonology/BrewDemon.lua
@@ -97,11 +97,11 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
--Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = {{
@@ -183,7 +183,7 @@ end
actionList.PreCombat = function()
if not unit.moving() and unit.level() >= 3
and br._G.GetTime() - br.pauseTime > 0.5
- and br.timer:useTimer("summonPet", 1) then
+ and br.debug.timer:useTimer("summonPet", 1) then
if mode.petSummon == 1 and activePet ~= pets.imp and cast.able.summonImp() and not cast.last.summonImp() then
if cast.summonImp("player") then return true end
end
@@ -359,8 +359,9 @@ local function runRotation()
end
end
local id = 266 -- Demonology Warlock
-if br.rotations[id] == nil then br.rotations[id] = {} end
-tinsert(br.rotations[id],{
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+tinsert(br.loader.rotations[id],{
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Warlock/Destruction/BrewDestro.lua b/Rotations/Warlock/Destruction/BrewDestro.lua
index 660c9ccfd8..441b99c387 100644
--- a/Rotations/Warlock/Destruction/BrewDestro.lua
+++ b/Rotations/Warlock/Destruction/BrewDestro.lua
@@ -62,11 +62,11 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
--Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -145,7 +145,7 @@ end -- End Action List - Defensive
actionList.PreCombat = function()
--actions.precombat+=/summon_pet
if not unit.moving() and unit.level() >= 3 and GetTime() - br.pauseTime > 0.5
- and br.timer:useTimer("summonPet", 1)
+ and br.debug.timer:useTimer("summonPet", 1)
then
if (mode.petSummon == 1 or (mode.petSummon == 2 and not spell.summonVoidwalker.known())) and not UnitExists("pet") then
if cast.summonImp("player") then return true end
@@ -182,7 +182,7 @@ local function runRotation()
power = br.player.power.soulShards()
-- General Locals
profileStop = profileStop or false
- haltProfile = (unit.inCombat() and profileStop) or IsMounted() or br.pause() or mode.rotation == 4
+ haltProfile = (unit.inCombat() and profileStop) or IsMounted() or br.functions.misc:pause() or mode.rotation == 4
-- Units
units.get(5) -- Makes a variable called, units.dyn5
units.get(40) -- Makes a variable called, units.dyn40
@@ -223,7 +223,7 @@ local function runRotation()
------------------------------
-- Start Attack
-- actions=auto_attack
- if not C_Spell.IsAutoRepeatSpell(GetSpellInfo(29722)) and unit.exists(units.dyn5) and unit.distance(units.dyn5) < 5 then
+ if not C_Spell.IsAutoRepeatSpell(br._G.GetSpellInfo(29722)) and unit.exists(units.dyn5) and unit.distance(units.dyn5) < 5 then
br._G.StartAttack(units.dyn5)
end
@@ -263,8 +263,9 @@ local function runRotation()
end -- Pause
end -- End runRotation
local id = 267 -- Change to the spec id profile is for.
-if br.rotations[id] == nil then br.rotations[id] = {} end
-tinsert(br.rotations[id], {
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Warlock/Initial/InitialWarlock.lua b/Rotations/Warlock/Initial/InitialWarlock.lua
index d73ea90dc8..654ea7b1cc 100644
--- a/Rotations/Warlock/Initial/InitialWarlock.lua
+++ b/Rotations/Warlock/Initial/InitialWarlock.lua
@@ -63,11 +63,11 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
--Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -106,7 +106,7 @@ local actionList = {}
actionList.Extras = function()
-- Summon Pet
if not unit.moving() and unit.level() >= 3 and GetTime() - br.pauseTime > 0.5
- and br.timer:useTimer("summonPet", 1)
+ and br.debug.timer:useTimer("summonPet", 1)
then
if (mode.petSummon == 1 or (mode.petSummon == 2 and not spell.summonVoidwalker.known())) and not pet.imp.active() then
if cast.summonImp("player") then return true end
@@ -290,8 +290,9 @@ local function runRotation()
end -- Pause
end -- End runRotation
local id = 1454 -- Change to the spec id profile is for.
-if br.rotations[id] == nil then br.rotations[id] = {} end
-tinsert(br.rotations[id], {
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Warlock/Support/PetCuteOne.lua b/Rotations/Warlock/Support/PetCuteOne.lua
index 1d0b2cabca..4f25adc9d5 100644
--- a/Rotations/Warlock/Support/PetCuteOne.lua
+++ b/Rotations/Warlock/Support/PetCuteOne.lua
@@ -1,5 +1,5 @@
-- Action List - Pet Management
-br.rotations.support["PetCuteOne"] = function()
+br.loader.rotations.support["PetCuteOne"] = function()
local function getCurrentPetMode()
local petMode = "None"
for i = 1, NUM_PET_ACTION_SLOTS do
@@ -26,7 +26,7 @@ br.rotations.support["PetCuteOne"] = function()
local units = br.player.units
-- General Locals
local profileStop = false
- local haltProfile = (inCombat and profileStop) or (IsMounted() or IsFlying()) or br.pause(true) or mode.rotation == 4
+ local haltProfile = (inCombat and profileStop) or (IsMounted() or IsFlying()) or br.functions.misc:pause(true) or mode.rotation == 4
-- Units
units.get(5)
units.get(40)
@@ -41,18 +41,18 @@ br.rotations.support["PetCuteOne"] = function()
enemies.get(40)
enemies.get(40, "player", true)
enemies.get(40, "player", false, true)
- enemies.yards40r = br.getEnemiesInRect(10, 40, false) or 0
+ enemies.yards40r = br.engines.enemiesEngineFunctions:getEnemiesInRect(10, 40, false) or 0
local petTarget
- if petTarget == nil or not UnitExists(petTarget) or not br.isValidUnit(petTarget) then
- if br.getOptionValue("Pet Target") == 1 and br.isValidUnit(units.dyn40) then
+ if petTarget == nil or not UnitExists(petTarget) or not br.functions.misc:isValidUnit(petTarget) then
+ if br.functions.misc:getOptionValue("Pet Target") == 1 and br.functions.misc:isValidUnit(units.dyn40) then
petTarget = units.dyn40
- elseif br.getOptionValue("Pet Target") == 2 and br.isValidUnit("target") then
+ elseif br.functions.misc:getOptionValue("Pet Target") == 2 and br.functions.misc:isValidUnit("target") then
petTarget = "target"
- elseif br.getOptionValue("Pet Target") == 3 then
+ elseif br.functions.misc:getOptionValue("Pet Target") == 3 then
for i = 1, #enemies.yards40 do
local thisUnit = enemies.yards40[i]
- if (br.isValidUnit(thisUnit) or br.isDummy()) then
+ if (br.functions.misc:isValidUnit(thisUnit) or br.functions.unit:isDummy()) then
petTarget = thisUnit
break
end
@@ -60,15 +60,15 @@ br.rotations.support["PetCuteOne"] = function()
end
end
- local friendUnit = br.friend[1].unit
+ local friendUnit = br.engines.healingEngine.friend[1].unit
local petActive = IsPetActive()
local petCombat = br._G.UnitAffectingCombat("pet")
- local petDistance = br.getDistance(petTarget, "pet") or 99
+ local petDistance = br.functions.range:getDistance(petTarget, "pet") or 99
local petExists = br._G.UnitExists("pet")
- local petHealth = br.getHP("pet")
+ local petHealth = br.functions.unit:getHP("pet")
local petMode = getCurrentPetMode()
local validTarget = br._G.UnitExists(petTarget) and
- (br.isValidUnit(petTarget) or br.isDummy()) --or (not UnitExists("pettarget") and br.isValidUnit("target")) or br.isDummy()
+ (br.functions.misc:isValidUnit(petTarget) or br.functions.unit:isDummy()) --or (not UnitExists("pettarget") and br.functions.misc:isValidUnit("target")) or br.functions.unit:isDummy()
-- if IsMounted() or IsFlying() or UnitHasVehicleUI("player") or CanExitVehicle("player") then
-- waitForPetToAppear = GetTime()
@@ -91,20 +91,20 @@ br.rotations.support["PetCuteOne"] = function()
-- waitForPetToAppear = GetTime()
-- end
-- end
- if br.isChecked("Pet - Auto Attack/Passive") then
+ if br.functions.misc:isChecked("Pet - Auto Attack/Passive") then
-- Set Pet Mode Out of Comat / Set Mode Passive In Combat
if inCombat and (petMode == "Defensive" or petMode == "Passive") and not haltProfile then
br._G.PetAssistMode()
elseif not inCombat and petMode == "Assist" and #enemies.yards40nc > 0 and not haltProfile then
br._G.PetDefensiveMode()
- elseif petMode ~= "Passive" and ((inCombat and #enemies.yards40 == 0) or haltProfile) and not br.isUnitCasting("player") then
+ elseif petMode ~= "Passive" and ((inCombat and #enemies.yards40 == 0) or haltProfile) and not br.functions.cast:isUnitCasting("player") then
br._G.PetPassiveMode()
end
-- Pet Attack / retreat
if (inCombat or petCombat) and not haltProfile then
br._G.PetAttack(petTarget)
- elseif not inCombat or (inCombat and not br.isValidUnit(petTarget)) or haltProfile
- and IsPetAttackActive() and not br.isUnitCasting("player")
+ elseif not inCombat or (inCombat and not br.functions.misc:isValidUnit(petTarget)) or haltProfile
+ and IsPetAttackActive() and not br.functions.cast:isUnitCasting("player")
then
br._G.PetStopAttack()
br._G.PetFollow()
@@ -112,19 +112,19 @@ br.rotations.support["PetCuteOne"] = function()
end
-- Manage Pet Abilities
-- -- Cat-like Refelexes / Spirit Mend / Survival of the Fittest
- -- if br.isChecked("Spirit Mend") and cast.able.spiritmend() and br.getHP(friendUnit) <= br.getOptionValue("Spirit Mend") then
+ -- if br.functions.misc:isChecked("Spirit Mend") and cast.able.spiritmend() and br.functions.unit:getHP(friendUnit) <= br.functions.misc:getOptionValue("Spirit Mend") then
-- if cast.spiritmend(friendUnit) then return end
-- end
- -- if br.isChecked("Cat-like Reflexes") and cast.able.catlikeReflexes() and petHealth <= br.getOptionValue("Cat-like Reflexes") then
+ -- if br.functions.misc:isChecked("Cat-like Reflexes") and cast.able.catlikeReflexes() and petHealth <= br.functions.misc:getOptionValue("Cat-like Reflexes") then
-- if cast.catlikeReflexes() then return end
-- end
- -- if br.isChecked("Survival of the Fittest") and cast.able.survivalOfTheFittest()
- -- --[[and petCombat ]]and petHealth <= br.getOptionValue("Survival of the Fittest")
+ -- if br.functions.misc:isChecked("Survival of the Fittest") and cast.able.survivalOfTheFittest()
+ -- --[[and petCombat ]]and petHealth <= br.functions.misc:getOptionValue("Survival of the Fittest")
-- then
-- if cast.survivalOfTheFittest() then return end
-- end
-- -- Bite/Claw
- -- if br.isChecked("Bite / Claw") and petCombat and validTarget and petDistance < 5 and not haltProfile and not br.isTotem(petTarget) then
+ -- if br.functions.misc:isChecked("Bite / Claw") and petCombat and validTarget and petDistance < 5 and not haltProfile and not br.engines.enemiesEngineFunctions:isTotem(petTarget) then
-- if cast.able.bite() then
-- if cast.bite(petTarget,"pet") then return end
-- end
@@ -133,18 +133,18 @@ br.rotations.support["PetCuteOne"] = function()
-- end
-- end
-- -- Dash
- -- if br.isChecked("Dash") and cast.able.dash() and validTarget and petDistance > 10 and br.getDistance(pertTarget) < 40 then
+ -- if br.functions.misc:isChecked("Dash") and cast.able.dash() and validTarget and petDistance > 10 and br.functions.range:getDistance(pertTarget) < 40 then
-- if cast.dash("pet") then return end
-- end
-- -- Purge
- -- if br.isChecked("Purge") and inCombat then
+ -- if br.functions.misc:isChecked("Purge") and inCombat then
-- if #enemies.yards5p > 0 then
-- local dispelled = false
-- local dispelledUnit = "player"
-- for i = 1, #enemies.yards5p do
-- local thisUnit = enemies.yards5p[i]
- -- if br.getOptionValue("Purge") == 1 or (br.getOptionValue("Purge") == 2 and UnitIsUnit(thisUnit,"target")) then
- -- if br.canDispel(thisUnit,spell.spiritShock) then
+ -- if br.functions.misc:getOptionValue("Purge") == 1 or (br.functions.misc:getOptionValue("Purge") == 2 and UnitIsUnit(thisUnit,"target")) then
+ -- if br.functions.aura:canDispel(thisUnit,spell.spiritShock) then
-- if cast.able.spiritShock(thisUnit,"pet") then
-- if cast.spiritShock(thisUnit,"pet") then dispelled = true; dispelledUnit = thisUnit; break end
-- elseif cast.able.chiJiTranq(thisUnit,"pet") then
@@ -167,11 +167,11 @@ br.rotations.support["PetCuteOne"] = function()
-- end
-- end
-- -- Growl
- -- if br.isChecked("Auto Growl") and inCombat then
+ -- if br.functions.misc:isChecked("Auto Growl") and inCombat then
-- local _, autoCastEnabled = C_Spell.GetSpellAutoCast(spell.growl)
-- if autoCastEnabled then C_Spell.SetSpellAutoCastEnabled(spell.growl) end
-- if not isTankInRange() and not buff.prowl.exists("pet") then
- -- if br.getOptionValue("Misdirection") == 3 and cast.able.misdirection("pet") and #enemies.yards8p > 1 then
+ -- if br.functions.misc:getOptionValue("Misdirection") == 3 and cast.able.misdirection("pet") and #enemies.yards8p > 1 then
-- if cast.misdirection("pet") then return end
-- end
-- if cast.able.growl() then
@@ -185,21 +185,21 @@ br.rotations.support["PetCuteOne"] = function()
-- end
-- end
-- -- Play Dead / Wake Up
- -- if br.isChecked("Play Dead / Wake Up") and not deadPet then
+ -- if br.functions.misc:isChecked("Play Dead / Wake Up") and not deadPet then
-- if cast.able.playDead() and petCombat and not buff.playDead.exists("pet")
- -- and petHealth < br.getOptionValue("Play Dead / Wake Up")
+ -- and petHealth < br.functions.misc:getOptionValue("Play Dead / Wake Up")
-- then
-- if cast.playDead() then return end
-- end
-- if cast.able.wakeUp() and buff.playDead.exists("pet") and not buff.feignDeath.exists()
- -- and petHealth >= br.getOptionValue("Play Dead / Wake Up")
+ -- and petHealth >= br.functions.misc:getOptionValue("Play Dead / Wake Up")
-- then
-- if cast.wakeUp() then return end
-- end
-- end
-- -- Prowl
- -- if br.isChecked("Prowl / Spirit Walk") and not petCombat
- -- and (not IsResting() or br.isDummy()) and #enemies.yards40nc > 0
+ -- if br.functions.misc:isChecked("Prowl / Spirit Walk") and not petCombat
+ -- and (not IsResting() or br.functions.unit:isDummy()) and #enemies.yards40nc > 0
-- then
-- if cast.able.spiritWalk() and not buff.spiritWalk.exists("pet") then
-- if cast.spiritWalk("pet") then return end
@@ -209,8 +209,8 @@ br.rotations.support["PetCuteOne"] = function()
-- end
-- end
-- -- Mend Pet
- -- if br.isChecked("Mend Pet") and cast.able.mendPet() and petExists and not deadPet
- -- and not buff.mendPet.exists("pet") and petHealth < br.getOptionValue("Mend Pet") and not haltProfile and not pause()
+ -- if br.functions.misc:isChecked("Mend Pet") and cast.able.mendPet() and petExists and not deadPet
+ -- and not buff.mendPet.exists("pet") and petHealth < br.functions.misc:getOptionValue("Mend Pet") and not haltProfile and not pause()
-- then
-- if cast.mendPet() then return end
-- end
diff --git a/Rotations/Warrior/Arms/ArmsCuteOne.lua b/Rotations/Warrior/Arms/ArmsCuteOne.lua
index 08d08bbacd..d165bba975 100644
--- a/Rotations/Warrior/Arms/ArmsCuteOne.lua
+++ b/Rotations/Warrior/Arms/ArmsCuteOne.lua
@@ -23,7 +23,7 @@ local function createToggles()
[1] = { mode = "Auto", value = 1, overlay = "Automatic Rotation", tip = "Swaps between Single and Multiple based on number of targets in range.", highlight = 1, icon = br.player.spells.cleave },
[2] = { mode = "Mult", value = 2, overlay = "Multiple Target Rotation", tip = "Multiple target rotation used.", highlight = 0, icon = br.player.spells.whirlwind },
[3] = { mode = "Sing", value = 3, overlay = "Single Target Rotation", tip = "Single target rotation used.", highlight = 0, icon = br.player.spells.mortalStrike },
- [4] = { mode = "Off", value = 4, overlay = "DPS Rotation Disabled", tip = "Disable DPS Rotation", highlight = 0, icon = br.player.spells.victoryRush }
+ [4] = { mode = "Off", value = 4, overlay = "DPS Rotation Disabled", tip = "Disarm DPS Rotation", highlight = 0, icon = br.player.spells.victoryRush }
};
br.ui:createToggle(RotationModes, "Rotation", 1, 0)
-- Cooldown Button
@@ -77,8 +77,12 @@ local function createOptions()
br.ui:createCheckbox(section, "Berserker Rage", "Check to use Berserker Rage")
-- Charge
br.ui:createCheckbox(section, "Charge", "Check to use Charge")
+ -- Heroic Leap
+ br.ui:createCheckbox(section, "Heroic Leap", "Check to use Heroic Leap")
-- Heroic Throw
br.ui:createCheckbox(section, "Heroic Throw", "Check to use Heroic Throw out of range")
+ -- Piercing Howl
+ br.ui:createCheckbox(section, "Piercing Howl", "Use Piercing Howl on moving enemies not facing you.")
-- Taunt
br.ui:createCheckbox(section, "Solo Taunt", "Only uses Taunt when not in a group.")
br.ui:checkSectionState(section)
@@ -86,26 +90,33 @@ local function createOptions()
--- COOLDOWN OPTIONS ---
------------------------
section = br.ui:createSection(br.ui.window.profile, "Cooldowns")
+ local alwaysCdNever = { "Always", "|cff0000ffCD", "|cffff0000Never" }
local alwaysCdAoENever = { "Always", "|cff008000AOE", "|cffffff00AOE/CD", "|cff0000ffCD", "|cffff0000Never" }
- -- Racial
- br.ui:createDropdownWithout(section, "Racial", alwaysCdAoENever, 4, "|cffFFFFFFWhen to use Racial.")
-- Avatar
- br.ui:createDropdownWithout(section, "Avatar", alwaysCdAoENever, 4, "|cffFFFFFFWhen to use Avatar.")
+ br.ui:createDropdownWithout(section, "Avatar", alwaysCdNever, 2, "|cffFFFFFFWhen to use Avatar.")
-- Bladestorm
br.ui:createDropdownWithout(section, "Bladestorm", alwaysCdAoENever, 3, "|cffFFFFFFWhen to use Bladestorm.")
- -- Champions Spear
- br.ui:createDropdownWithout(section, "Champion's Spear", alwaysCdAoENever, 3,
- "|cffFFFFFFWhen to use Champion's Spear.")
- -- Colossus Smash / Warbreaker
- br.ui:createDropdownWithout(section, "Colossus Smash / Warbreaker", alwaysCdAoENever, 4,
- "|cffFFFFFFWhen to use Colossus Smash / Warbreaker.")
- -- Demolish
- br.ui:createDropdownWithout(section, "Demolish", alwaysCdAoENever, 3, "|cffFFFFFFWhen to use Demolish.")
- -- Ravager
- br.ui:createDropdownWithout(section, "Ravager", alwaysCdAoENever, 3, "|cffFFFFFFWhen to use Ravager.")
- -- Thunderous Roar
- br.ui:createDropdownWithout(section, "Thunderous Roar", alwaysCdAoENever, 3,
- "|cffFFFFFFWhen to use Thunderous Roar.")
+ -- Bloodbath
+ br.ui:createDropdownWithout(section, "Bloodbath", alwaysCdNever, 2,
+ "|cffFFFFFFWhen to use Bloodbath.")
+ -- Colossus Smash
+ br.ui:createDropdownWithout(section, "Colossus Smash", alwaysCdNever, 1,
+ "|cffFFFFFFWhen to use Colossus Smash.")
+ -- Dragon Roar
+ br.ui:createDropdownWithout(section, "Dragon Roar", alwaysCdAoENever, 3,
+ "|cffFFFFFFWhen to use Dragon Roar.")
+ -- Recklessness
+ br.ui:createDropdownWithout(section, "Recklessness", alwaysCdAoENever, 3,
+ "|cffFFFFFFWhen to use Recklessness.")
+ -- Skull Banner
+ br.ui:createDropdownWithout(section, "Skull Banner", alwaysCdNever, 2,
+ "|cffFFFFFFWhen to use Skull Banner.")
+ -- Shattering Throw
+ br.ui:createDropdownWithout(section, "Shattering Throw", alwaysCdNever, 2,
+ "|cffFFFFFFWhen to use Shattering Throw.")
+ -- Storm Bolt
+ br.ui:createDropdownWithout(section, "Storm Bolt", alwaysCdNever, 2,
+ "|cffFFFFFFWhen to use Storm Bolt.")
br.ui:checkSectionState(section)
-------------------------
--- DEFENSIVE OPTIONS ---
@@ -114,14 +125,16 @@ local function createOptions()
local defTooltip = "|cffFFBB00Health Percentage to use at."
-- Defensive Stance
br.ui:createSpinner(section, "Defensive Stance", 60, 0, 100, 5, defTooltip)
+ -- Disarm (CC fleeing enemies)
+ br.ui:createCheckbox(section, "Disarm", "Use Disarm on fleeing enemies")
-- Die By The Sword
br.ui:createSpinner(section, "Die By The Sword", 60, 0, 100, 5, defTooltip)
- -- Ignore Pain
- br.ui:createSpinner(section, "Ignore Pain", 60, 0, 100, 5, defTooltip)
-- Intimidating Shout
br.ui:createSpinner(section, "Intimidating Shout", 60, 0, 100, 5, defTooltip)
-- Rallying Cry
br.ui:createSpinner(section, "Rallying Cry", 60, 0, 100, 5, defTooltip)
+ -- Shield Wall
+ br.ui:createSpinner(section, "Shield Wall", 30, 0, 100, 5, defTooltip)
-- Victory Rush
br.ui:createSpinner(section, "Victory Rush", 60, 0, 100, 5, defTooltip)
br.ui:checkSectionState(section)
@@ -134,7 +147,7 @@ local function createOptions()
-- Intimidating Shout
br.ui:createCheckbox(section, "Intimidating Shout - Int")
-- Storm Bolt Logic
- br.ui:createCheckbox(section, "Storm Bolt Logic", "Stun specific Spells and Mobs")
+ br.ui:createCheckbox(section, "Storm Bolt - Int", "Stun specific Spells and Mobs")
-- Interrupt Percentage
br.ui:createSpinner(section, "Interrupt At", 70, 0, 95, 5, "|cffFFBB00Cast Percentage to use at.")
br.ui:checkSectionState(section)
@@ -143,17 +156,17 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
--Cooldown Key Toggle
- br.ui:createDropdownWithout(section, "Cooldown Mode", br.dropOptions.Toggle, 3)
+ br.ui:createDropdownWithout(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 3)
--Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupts Key Toggle
- br.ui:createDropdownWithout(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
-- Mover Toggle
- br.ui:createDropdownWithout(section, "Mover Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Mover Mode", br.ui.dropOptions.Toggle, 6)
-- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ -- br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = {
@@ -178,10 +191,12 @@ local equiped
local module
local race
local rage
+local spell
local talent
local ui
local unit
local units
+local use
local var
--------------------
@@ -201,32 +216,48 @@ actionList.Extras = function()
end
end
end -- End Dummy Test
- -- Battle Shout
- if ui.checked("Battle Shout") and cast.able.battleShout() then
- for i = 1, #br.friend do
- local thisUnit = br.friend[i].unit
- if not unit.deadOrGhost(thisUnit) and unit.distance(thisUnit) < 100 and buff.battleShout.remain(thisUnit) < 600 then
- if cast.battleShout() then
- ui.debug("Casting Battle Shout")
- return true
- end
+ -- Battle Stance
+ if cast.able.battleStance() then
+ if unit.form() ~= 1 and (not ui.checked("Defensive Stance") or (unit.hp("player") > ui.value("Defensive Stance"))) then
+ if cast.battleStance() then
+ ui.debug("Casting Battle Stance [Extra]")
+ return true
end
end
end
-- Berserker Rage
if ui.checked("Berserker Rage") and cast.able.berserkerRage() and cast.noControl.berserkerRage() then
if cast.berserkerRage() then
- ui.debug("Casting Berserker Rage")
+ ui.debug("Casting Berserker Rage [Extra]")
return true
end
end
+ -- Disarm: mirror Grapple Weapon logic (with blacklist on error)
+ if ui.checked("Disarm") and unit.inCombat() and #enemies.yards5f > 0 then
+ for i = 1, #enemies.yards5f do
+ local thisUnit = enemies.yards5f[i]
+ if cast.able.disarm(thisUnit) and unit.exists(thisUnit) and unit.distance(thisUnit) <= 5
+ and (not debuff.disarm.exists(thisUnit))
+ then
+ -- Get NPC ID to check blacklist
+ local npcID = br.functions.unit:GetObjectID(thisUnit)
+ if not var.disarmBlacklist[npcID] then
+ var.lastDisarmTarget = thisUnit
+ if cast.disarm(thisUnit) then
+ ui.debug("Casting Disarm on " .. unit.name(thisUnit) .. " [Extra]")
+ return true
+ end
+ end
+ end
+ end
+ end
-- Piercing Howl
if ui.checked("Piercing Howl") then
for i = 1, #enemies.yards15 do
local thisUnit = enemies.yards15[i]
if cast.able.piercingHowl(thisUnit) and unit.moving(thisUnit) and not unit.facing(thisUnit, "player") and unit.distance(thisUnit) >= 5 then
if cast.piercingHowl(thisUnit) then
- ui.debug("Casting Piercing Howl")
+ ui.debug("Casting Piercing Howl [Extra]")
return true
end
end
@@ -238,20 +269,15 @@ end -- End Action List - Extra
actionList.Defensive = function()
if ui.useDefensive() then
local useDefensive = function(op)
- return unit.inCombat() and unit.hp() <= ui.value(op)
+ return unit.inCombat() and unit.hp("player") <= ui.value(op)
end
-- Basic Healing Module
module.BasicHealing()
-- Defensive Stance
if ui.checked("Defensive Stance") and cast.able.defensiveStance() then
- if useDefensive("Defensive Stance") and not buff.defensiveStance.exists() then
+ if unit.form() ~= 2 and unit.hp("player") <= ui.value("Defensive Stance") then
if cast.defensiveStance() then
- ui.debug("Casting Defensive Stance")
- return
- end
- elseif buff.defensiveStance.exists() and unit.hp() > ui.value("Defensive Stance") then
- if cast.defensiveStance() then
- ui.debug("Casting Defensive Stance")
+ ui.debug("Casting Defensive Stance [Defensive]")
return
end
end
@@ -259,28 +285,28 @@ actionList.Defensive = function()
-- Die By The Sword
if ui.checked("Die By The Sword") and cast.able.dieByTheSword() and useDefensive("Die By The Sword") then
if cast.dieByTheSword() then
- ui.debug("Casting Die By The Sword")
+ ui.debug("Casting Die By The Sword [Defensive]")
return
end
end
- -- Ignore Pain
- if ui.checked("Ignore Pain") and cast.able.ignorePain() and useDefensive("Ignore Pain") and rage() >= 40 then
- if cast.ignorePain() then
- ui.debug("Casting Ignore Pain")
- return true
- end
- end
-- Intimidating Shout
if ui.checked("Intimidating Shout") and cast.able.intimidatingShout() and useDefensive("Intimidating Shout") then
if cast.intimidatingShout() then
- ui.debug("Casting Intimidating Shout")
+ ui.debug("Casting Intimidating Shout [Defensive]")
return true
end
end
-- Rallying Cry
if ui.checked("Rallying Cry") and cast.able.rallyingCry() and useDefensive("Rallying Cry") then
if cast.rallyingCry() then
- ui.debug("Casting Rallying Cry")
+ ui.debug("Casting Rallying Cry [Defensive]")
+ return true
+ end
+ end
+ -- Shield Wall
+ if ui.checked("Shield Wall") and cast.able.shieldWall() and useDefensive("Shield Wall") then
+ if cast.shieldWall() then
+ ui.debug("Casting Shield Wall [Defensive]")
return true
end
end
@@ -290,7 +316,7 @@ actionList.Defensive = function()
local thisUnit = enemies.yards40[i]
if (cast.timeRemain(thisUnit) < 5 or cast.inFlight.spellReflection(thisUnit)) then
if cast.spellReflection() then
- ui.debug("Casting Spell Reflection")
+ ui.debug("Casting Spell Reflection [Defensive]")
return true
end
end
@@ -299,33 +325,39 @@ actionList.Defensive = function()
-- Storm Bolt
if ui.checked("Storm Bolt") and cast.able.stormBolt() and useDefensive("Storm Bolt") then
if cast.stormBolt() then
- ui.debug("Casting Storm Bolt")
+ ui.debug("Casting Storm Bolt [Defensive]")
return true
end
end
- -- Victory Rush
- if ui.checked("Victory Rush") and (cast.able.victoryRush() or cast.able.impendingVictory())
- and unit.inCombat() and buff.victorious.exists()
- then
+ -- Victory Rush / Impending Victory (defensive heal)
+ if ui.checked("Victory Rush") and unit.inCombat() and (buff.victorious.exists() or talent.impendingVictory) then
for i = 1, #enemies.yards5f do
local thisUnit = enemies.yards5f[i]
- if unit.hp() <= ui.value("Victory Rush") or buff.victorious.remain() < unit.gcd(true) or unit.ttd(thisUnit) < unit.gcd(true) then
+ if unit.hp("player") <= ui.value("Victory Rush") or (buff.victorious.exists()
+ and (buff.victorious.remain() < unit.gcd(true) or unit.ttd(thisUnit) < unit.gcd(true)))
+ then
if talent.impendingVictory then
- if cast.impendingVictory(thisUnit) then
- ui.debug("Casting Impending Victory")
- return true
+ -- Replacement: use Impending Victory (does not require victorious)
+ if cast.able.impendingVictory(thisUnit) then
+ if cast.impendingVictory(thisUnit) then
+ ui.debug("Casting Impending Victory [Defensive]")
+ return true
+ end
end
else
- if cast.victoryRush(thisUnit) then
- ui.debug("Casting Victory Rush")
- return true
+ -- No talent: use Victory Rush only when buff exists
+ if buff.victorious.exists() and cast.able.victoryRush(thisUnit) then
+ if cast.victoryRush(thisUnit) then
+ ui.debug("Casting Victory Rush [Defensive]")
+ return true
+ end
end
end
end
end
end
end -- End Defensive Check
-end -- End Action List - Defensive
+end -- End Action List - Defensive
-- Action List - Interrupts
actionList.Interrupts = function()
@@ -337,7 +369,7 @@ actionList.Interrupts = function()
-- Pummel
if ui.checked("Pummel") and cast.able.pummel(thisUnit) and distance < 5 then
if cast.pummel(thisUnit) then
- ui.debug("Casting Pummel")
+ ui.debug("Casting Pummel [Interrupt]")
return true
end
end
@@ -360,383 +392,165 @@ actionList.Interrupts = function()
end
end -- End Action List - Interrupts
--- Action List - ColossusAoe
-actionList.ColossusAoe = function()
- -- Cleave
- -- cleave,if=buff.collateral_damage.up&buff.merciless_bonegrinder.up
- if cast.able.cleave("player", "cone", 1, 8) and buff.collateralDamage.exists() and buff.mercilessBonegrinder.exists() then
- if cast.cleave("player", "cone", 1, 8) then
- ui.debug("Casting Cleave - Collateral Damage [Colossus Aoe]")
- return true
- end
- end
- -- Thunder Clap
- -- thunder_clap,if=!dot.rend.remains
- if cast.able.thunderClap("player", "aoe", 1, 8) and not debuff.rend.exists(units.dyn5) then
- if cast.thunderClap("player", "aoe", 1, 8) then
- ui.debug("Casting Thunder Clap - No Rend [Colossus Aoe]")
- return true
- end
- end
- -- Thunderous Roar
- -- thunderous_roar
- if ui.alwaysCdAoENever("Thunderous Roar") and cast.able.thunderousRoar("player", "aoe", 1, 12) then
- if cast.thunderousRoar() then
- ui.debug("Casting Thunderous Roar [Colossus Aoe]")
- return true
- end
- end
- -- Avatar
- -- avatar
- if ui.alwaysCdAoENever("Avatar") and cast.able.avatar() then
- if cast.avatar() then
- ui.debug("Casting Avatar [Colossus Aoe]")
- return true
- end
- end
- -- Ravager
- -- ravager
- if ui.alwaysCdAoENever("Ravager", 1, 8) and cast.able.ravager("target", "ground", 1, 8) then
- if cast.ravager("target", "ground") then
- ui.debug("Casting Ravager [Colossus Aoe]")
- return true
- end
- end
+-- Action List - AOE
+actionList.AOE = function()
-- Sweeping Strikes
-- sweeping_strikes
- if cast.able.sweepingStrikes() then
+ if cast.able.sweepingStrikes() and not buff.sweepingStrikes.exists() then
if cast.sweepingStrikes() then
- ui.debug("Casting Sweeping Strikes [Colossus Aoe]")
+ ui.debug("Casting Sweeping Strikes [AOE]")
return true
end
end
- -- Skullsplitter
- -- skullsplitter,if=buff.sweeping_strikes.up
- if cast.able.skullsplitter() and buff.sweepingStrikes.exists() then
- if cast.skullsplitter() then
- ui.debug("Casting Skullsplitter [Colossus Aoe]")
- return true
- end
- end
- -- Warbreaker
- -- warbreaker
- if ui.alwaysCdAoENever("Colossus Smash / Warbreaker", 1, 8) and cast.able.warbreaker("player", "aoe", 1, 8) then
- if cast.warbreaker("player", "aoe", 1, 8) then
- ui.debug("Casting Warbreaker [Colossus Aoe]")
+ -- Cleave
+ -- cleave,if=rage>110&active_enemies<=4
+ if cast.able.cleave(units.dyn5,"cone",1,8) and (rage() > 110 or (rage.max() == 100 and rage() > 90)) and #enemies.yards5f <= 4 then
+ if cast.cleave("player","cone",1,8) then
+ ui.debug("Casting Cleave [AOE]")
return true
end
end
-- Bladestorm
- -- bladestorm,if=talent.unhinged|talent.merciless_bonegrinder
- if ui.alwaysCdAoENever("Bladestorm", 3, 8) and cast.able.bladestorm("player", "aoe", 1, 8) and ((talent.unhinged or talent.mercilessBonegrinder)) then
- if cast.bladestorm("player", "aoe", 1, 8) then
- ui.debug("Casting Bladestorm - Unhinged/Merciless Bonegrinder [Colossus Aoe]")
+ -- bladestorm,if=enabled&(buff.bloodbath.up|!talent.bloodbath.enabled)
+ if ui.alwaysCdAoENever("Bladestorm",3,#enemies.yards8) and cast.able.bladestorm("player","aoe",1,8)
+ and (buff.bloodbath.exists() or not talent.bloodbath)
+ then
+ if cast.bladestorm("player","aoe",1,8) then
+ ui.debug("Casting Bladestorm [Single]")
return true
end
end
- -- Champions Spear
- -- champions_spear
- if ui.alwaysCdAoENever("Champion's Spear", 3, 8) and cast.able.championsSpear("target", "ground", 1, 8) then
- if cast.championsSpear("target", "ground", 1, 8) then
- ui.debug("Casting Champions Spear [Colossus Aoe]")
+ -- Dragon Roar
+ -- dragon_roar,if=enabled&debuff.colossus_smash.down
+ if ui.alwaysCdAoENever("Dragon Roar",3,#enemies.yards8) and cast.able.dragonRoar("player","aoe",1,8)
+ and not debuff.colossusSmash.exists(units.dyn5)
+ then
+ if cast.dragonRoar("player","aoe",1,8) then
+ ui.debug("Casting Dragon Roar [Single]")
return true
end
end
-- Colossus Smash
- -- colossus_smash
- if ui.alwaysCdAoENever("Colossus Smash / Warbreaker", 1, 5) and cast.able.colossusSmash() then
+ -- colossus_smash,if=debuff.colossus_smash.remains<1
+ if ui.alwaysCdNever("Colossus Smash") and cast.able.colossusSmash()
+ and debuff.colossusSmash.remains(units.dyn5) < 1
+ then
if cast.colossusSmash() then
- ui.debug("Casting Colossus Smash [Colossus Aoe]")
- return true
- end
- end
- -- Cleave
- -- cleave
- if cast.able.cleave("player", "cone", 1, 8) then
- if cast.cleave("player", "cone", 1, 8) then
- ui.debug("Casting Cleave [Colossus Aoe]")
- return true
- end
- end
- -- Demolish
- -- demolish,if=buff.sweeping_strikes.up
- if ui.alwaysCdAoENever("Demolish", 3, 8) and cast.able.demolish("player", "aoe", 1, 8) and buff.sweepingStrikes.exists() then
- if cast.demolish("player", "aoe", 1, 8) then
- ui.debug("Casting Demolish [Colossus Aoe]")
- return true
- end
- end
- -- Bladestorm
- -- bladestorm,if=talent.unhinged
- if ui.alwaysCdAoENever("Bladestorm", 3, 8) and cast.able.bladestorm("player", "aoe", 1, 8) and talent.unhinged then
- if cast.bladestorm("player", "aoe", 1, 8) then
- ui.debug("Casting Bladestorm - Unhinged [Colossus Aoe]")
- return true
- end
- end
- -- Overpower
- -- overpower
- if cast.able.overpower() then
- if cast.overpower() then
- ui.debug("Casting Overpower [Colossus Aoe]")
- return true
- end
- end
- -- Mortal Strike
- -- mortal_strike,if=buff.sweeping_strikes.up
- if cast.able.mortalStrike() and buff.sweepingStrikes.exists() then
- if cast.mortalStrike() then
- ui.debug("Casting Mortal Strike - Sweeping Strikes [Colossus Aoe]")
- return true
- end
- end
- -- Overpower
- -- overpower,if=buff.sweeping_strikes.up
- if cast.able.overpower() and buff.sweepingStrikes.exists() then
- if cast.overpower() then
- ui.debug("Casting Overpower - Sweeping Strikes [Colossus Aoe]")
- return true
- end
- end
- -- Execute
- -- execute,if=buff.sweeping_strikes.up
- if cast.able.execute() and buff.sweepingStrikes.exists() then
- if cast.execute() then
- ui.debug("Casting Execute - Sweeping Strikes [Colossus Aoe]")
+ ui.debug("Casting Colossus Smash [Single]")
return true
end
end
-- Thunder Clap
- -- thunder_clap
- if cast.able.thunderClap("player", "aoe", 1, 8) then
- if cast.thunderClap("player", "aoe", 1, 8) then
- ui.debug("Casting Thunder Clap [Colossus Aoe]")
+ -- thunder_clap,target=2,if=dot.deep_wounds.attack_power*1.1= 2 then
+ if cast.thunderClap("player","aoe",1,8) then
+ ui.debug("Casting Thunder Clap [AOE]")
return true
end
end
-- Mortal Strike
- -- mortal_strike
- if cast.able.mortalStrike() then
+ -- mortal_strike,if=active_enemies=2|rage<50
+ if cast.able.mortalStrike() and (#enemies.yards5f == 2 or rage() < 50) then
if cast.mortalStrike() then
- ui.debug("Casting Mortal Strike [Colossus Aoe]")
+ ui.debug("Casting Mortal Strike [AOE]")
return true
end
end
-- Execute
- -- execute
- if cast.able.execute() then
+ -- execute,if=buff.sudden_execute.down&active_enemies=2
+ if cast.able.execute() and not buff.suddenExecute.exists()
+ and #enemies.yards5f == 2 and unit.hp(units.dyn5) < 20
+ then
if cast.execute() then
- ui.debug("Casting Execute [Colossus Aoe]")
- return true
- end
- end
- -- Bladestorm
- -- bladestorm
- if ui.alwaysCdAoENever("Bladestorm", 3, 8) and cast.able.bladestorm("player", "aoe", 1, 8) then
- if cast.bladestorm("player", "aoe", 1, 8) then
- ui.debug("Casting Bladestorm [Colossus Aoe]")
- return true
- end
- end
- -- Whirlwind
- -- whirlwind
- if cast.able.whirlwind("player", "aoe", 1, 8) then
- if cast.whirlwind("player", "aoe", 1, 8) then
- ui.debug("Casting Whirlwind [Colossus Aoe]")
- return true
- end
- end
-end -- End Action List - ColossusAoe
-
--- Action List - ColossusExecute
-actionList.ColossusExecute = function()
- -- Sweeping Strikes
- -- sweeping_strikes,if=active_enemies=2
- if cast.able.sweepingStrikes() and #enemies.yards5f == 2 then
- if cast.sweepingStrikes() then
- ui.debug("Casting Sweeping Strikes [Colossus Execute]")
+ ui.debug("Casting Execute [AOE]")
return true
end
end
- -- Rend
- -- rend,if=dot.rend.remains<=gcd&!talent.bloodletting
- if cast.able.rend(var.minHpUnit) and debuff.rend.remains(units.dyn5) <= unit.gcd(true) and not talent.bloodletting then
- if cast.rend(var.minHpUnit) then
- ui.debug("Casting Rend [Colossus Execute]")
- return true
- end
- end
- -- Thunderous Roar
- -- thunderous_roar
- if ui.alwaysCdAoENever("Thunderous Roar") and cast.able.thunderousRoar("player", "aoe", 1, 12) then
- if cast.thunderousRoar("player", "aoe", 1, 12) then
- ui.debug("Casting Thunderous Roar [Colossus Execute]")
- return true
- end
- end
- -- Champions Spear
- -- champions_spear
- if ui.alwaysCdAoENever("Champion's Spear", 3, 8) and cast.able.championsSpear(var.minHpUnit, "ground", 1, 8) then
- if cast.championsSpear(var.minHpUnit, "ground", 1, 8) then
- ui.debug("Casting Champions Spear [Colossus Execute]")
- return true
- end
- end
- -- Ravager
- -- ravager,if=cooldown.colossus_smash.remains<=gcd
- if ui.alwaysCdAoENever("Ravager", 1, 8) and cast.able.ravager(var.minHpUnit, "ground", 1, 8) and cd.colossusSmash.remains() <= unit.gcd(true) then
- if cast.ravager(var.minHpUnit, "ground", 1, 8) then
- ui.debug("Casting Ravager [Colossus Execute]")
- return true
- end
- end
- -- Avatar
- -- avatar
- if ui.alwaysCdAoENever("Avatar") and cast.able.avatar() then
- if cast.avatar() then
- ui.debug("Casting Avatar [Colossus Execute]")
- return true
- end
- end
- -- Colossus Smash
- -- colossus_smash
- if ui.alwaysCdAoENever("Colossus Smash / Warbreaker", 1, 5) and cast.able.colossusSmash(var.minHpUnit) then
- if cast.colossusSmash(var.minHpUnit) then
- ui.debug("Casting Colossus Smash [Colossus Execute]")
- return true
- end
- end
- -- Warbreaker
- -- warbreaker
- if ui.alwaysCdAoENever("Colossus Smash / Warbreaker", 1, 8) and cast.able.warbreaker("player", "aoe", 1, 8) then
- if cast.warbreaker("player", "aoe", 1, 8) then
- ui.debug("Casting Warbreaker [Colossus Execute]")
- return true
- end
- end
- -- Demolish
- -- demolish,if=debuff.colossus_smash.up
- if ui.alwaysCdAoENever("Demolish", 3, 8) and cast.able.demolish("player", "aoe", 1, 8) and debuff.colossusSmash.exists(var.minHpUnit) then
- if cast.demolish("player", "aoe", 1, 8) then
- ui.debug("Casting Demolish [Colossus Execute]")
- return true
- end
- end
- -- Mortal Strike
- -- mortal_strike,if=debuff.executioners_precision.stack=2&!dot.ravager.remains&(buff.lethal_blows.stack=2|!set_bonus.tww1_4pc)
- if cast.able.mortalStrike(var.minHpUnit) and ((debuff.executionersPrecision.count(var.minHpUnit) == 2 and not debuff.ravager.remains(units.dyn5)
- and (buff.lethalBlows.stack() == 2 or not equiped.tier("TWW1", 4))))
+ -- Slam
+ -- slam,if=buff.sweeping_strikes.up&debuff.colossus_smash.up
+ if cast.able.slam() and buff.sweepingStrikes.exists()
+ and debuff.colossusSmash.exists(units.dyn5)
then
- if cast.mortalStrike(var.minHpUnit) then
- ui.debug("Casting Mortal Strike - Executioner's Precision [Colossus Execute]")
- return true
- end
- end
- -- Execute
- -- execute,if=rage>=40
- if cast.able.execute(var.minHpUnit) and rage() >= 40 then
- if cast.execute(var.minHpUnit) then
- ui.debug("Casting Execute - High Rage [Colossus Execute]")
- return true
- end
- end
- -- Skullsplitter
- -- skullsplitter
- if cast.able.skullsplitter(var.minHpUnit) then
- if cast.skullsplitter(var.minHpUnit) then
- ui.debug("Casting Skullsplitter [Colossus Execute]")
+ if cast.slam() then
+ ui.debug("Casting Slam [AOE]")
return true
end
end
-- Overpower
- -- overpower
- if cast.able.overpower(var.minHpUnit) then
- if cast.overpower(var.minHpUnit) then
- ui.debug("Casting Overpower [Colossus Execute]")
+ -- overpower,if=active_enemies=2
+ if cast.able.overpower() and #enemies.yards5f == 2 then
+ if cast.overpower() then
+ ui.debug("Casting Overpower [AOE]")
return true
end
end
- -- Bladestorm
- -- bladestorm
- if ui.alwaysCdAoENever("Bladestorm", 3, 8) and cast.able.bladestorm("player", "aoe", 1, 8) then
- if cast.bladestorm("player", "aoe", 1, 8) then
- ui.debug("Casting Bladestorm [Colossus Execute]")
+ -- Whirlwind
+ if cast.able.whirlwind("player","aoe",1,8) and not spell.sweepingStrikes.known()
+ and rage() > 40
+ then
+ if cast.whirlwind("player","aoe",1,8) then
+ ui.debug("Casting Whirlwind [AOE]")
return true
end
end
- -- Execute
- -- execute
- if cast.able.execute(var.minHpUnit) then
- if cast.execute(var.minHpUnit) then
- ui.debug("Casting Execute [Colossus Execute]")
+ -- Slam
+ -- slam,if=buff.sweeping_strikes.up
+ if cast.able.slam() and (buff.sweepingStrikes.exists() or not spell.sweepingStrikes.known()) then
+ if cast.slam() then
+ ui.debug("Casting Slam [AOE]")
return true
end
end
- -- Mortal Strike
- -- mortal_strike
- if cast.able.mortalStrike(var.minHpUnit) then
- if cast.mortalStrike(var.minHpUnit) then
- ui.debug("Casting Mortal Strike [Colossus Execute]")
+ -- Battle Shout
+ -- battle_shout
+ if cast.able.battleShout() and not buff.battleShout.exists() and rage.max() - rage() > 20 then
+ if cast.battleShout() then
+ ui.debug("Casting Battle Shout [AOE]")
return true
end
end
-end -- End Action List - ColossusExecute
+end -- End Action List - AOE
--- Action List - ColossusSt
-actionList.ColossusSt = function()
- -- Rend
- -- rend,if=dot.rend.remains<=gcd
- if cast.able.rend() and debuff.rend.remains(units.dyn5) <= unit.gcd(true) then
- if cast.rend() then
- ui.debug("Casting Rend - Reapply Now [Colossus St]")
- return true
- end
- end
- -- Thunderous Roar
- -- thunderous_roar
- if ui.alwaysCdAoENever("Thunderous Roar") and cast.able.thunderousRoar("player", "aoe", 1, 12) then
- if cast.thunderousRoar("player", "aoe", 1, 12) then
- ui.debug("Casting Thunderous Roar [Colossus St]")
- return true
- end
- end
- -- Champions Spear
- -- champions_spear
- if ui.alwaysCdAoENever("Champion's Spear", 3, 8) and cast.able.championsSpear("target", "ground", 1, 8) then
- if cast.championsSpear("target", "ground", 1, 8) then
- ui.debug("Casting Champions Spear [Colossus St]")
- return true
- end
- end
- -- Ravager
- -- ravager,if=cooldown.colossus_smash.remains<=gcd
- if ui.alwaysCdAoENever("Ravager", 1, 8) and cast.able.ravager("target", "ground", 1, 8) and cd.colossusSmash.remains() <= unit.gcd(true) then
- if cast.ravager("target", "ground", 1, 8) then
- ui.debug("Casting Ravager [Colossus St]")
+-- Action List - Single
+actionList.Single = function()
+ -- Heroic Strike
+ -- heroic_strike,if=rage>115|(debuff.colossus_smash.up&rage>60&set_bonus.tier16_2pc_melee)
+ if cast.able.heroicStrike()
+ and ((rage() > 115 or (rage.max() == 100 and rage() > 95)) or (debuff.colossusSmash.exists(units.dyn5)
+ and rage() > 60 and equiped.tier(16,2)))
+ then
+ if cast.heroicStrike() then
+ ui.debug("Casting Heroic Strike [Single]")
return true
end
end
- -- Avatar
- -- avatar,if=raid_event.adds.in>15
- if ui.alwaysCdAoENever("Avatar") and cast.able.avatar() then
- if cast.avatar() then
- ui.debug("Casting Avatar [Colossus St]")
+ -- Mortal Strike
+ -- mortal_strike,if=dot.deep_wounds.remains<1.0|buff.enrage.down|rage<10
+ if cast.able.mortalStrike()
+ and (debuff.deepWounds.remains(units.dyn5) < 1.0
+ or not buff.enrage.exists() or rage() < 10)
+ then
+ if cast.mortalStrike() then
+ ui.debug("Casting Mortal Strike [Single]")
return true
end
end
-- Colossus Smash
- -- colossus_smash
- if ui.alwaysCdAoENever("Colossus Smash / Warbreaker", 1, 5) and cast.able.colossusSmash() then
+ -- colossus_smash,if=debuff.colossus_smash.remains<1.0
+ if ui.alwaysCdNever("Colossus Smash") and cast.able.colossusSmash()
+ and debuff.colossusSmash.remains(units.dyn5) < 1.0
+ then
if cast.colossusSmash() then
- ui.debug("Casting Colossus Smash [Colossus St]")
+ ui.debug("Casting Colossus Smash [Single]")
return true
end
end
- -- Warbreaker
- -- warbreaker
- if ui.alwaysCdAoENever("Colossus Smash / Warbreaker", 1, 8) and cast.able.warbreaker("player", "aoe", 1, 8) then
- if cast.warbreaker("player", "aoe", 1, 8) then
- ui.debug("Casting Warbreaker [Colossus St]")
+ -- Bladestorm
+ --# Use cancelaura (in-game) to stop bladestorm if CS comes off cooldown during it for any reason.
+ -- bladestorm,if=enabled,interrupt_if=!cooldown.colossus_smash.remains
+ if ui.alwaysCdAoENever("Bladestorm",3,#enemies.yards8) and cast.able.bladestorm("player","aoe",1,8) and (not var.knownCS or cd.colossusSmash.remains(units.dyn5) > 10) then
+ if cast.bladestorm("player","aoe",1,8) then
+ ui.debug("Casting Bladestorm [Single]")
return true
end
end
@@ -744,967 +558,93 @@ actionList.ColossusSt = function()
-- mortal_strike
if cast.able.mortalStrike() then
if cast.mortalStrike() then
- ui.debug("Casting Mortal Strike [Colossus St]")
- return true
- end
- end
- -- Demolish
- -- demolish
- if ui.alwaysCdAoENever("Demolish", 3, 8) and cast.able.demolish("player", "aoe", 1, 8) then
- if cast.demolish("player", "aoe", 1, 8) then
- ui.debug("Casting Demolish [Colossus St]")
+ ui.debug("Casting Mortal Strike [Single]")
return true
end
end
- -- Skullsplitter
- -- skullsplitter
- if cast.able.skullsplitter() then
- if cast.skullsplitter() then
- ui.debug("Casting Skullsplitter [Colossus St]")
+ -- Storm Bolt
+ -- storm_bolt,if=enabled&debuff.colossus_smash.up
+ if ui.alwaysCdNever("Storm Bolt") and cast.able.stormBolt()
+ and debuff.colossusSmash.exists(units.dyn5)
+ then
+ if cast.stormBolt() then
+ ui.debug("Casting Storm Bolt [Single]")
return true
end
end
- -- Overpower
- -- overpower,if=charges=2
- if cast.able.overpower() and charges == 2 then
- if cast.overpower() then
- ui.debug("Casting Overpower - Max Charges [Colossus St]")
+ -- Dragon Roar
+ -- dragon_roar,if=enabled&debuff.colossus_smash.down
+ if ui.alwaysCdAoENever("Dragon Roar",3,#enemies.yards8) and cast.able.dragonRoar("player","aoe",1,8)
+ and not debuff.colossusSmash.exists(units.dyn5)
+ then
+ if cast.dragonRoar("player","aoe",1,8) then
+ ui.debug("Casting Dragon Roar [Single]")
return true
end
end
-- Execute
- -- execute
- if cast.able.execute() then
+ -- execute,if=buff.sudden_execute.down|buff.taste_for_blood.down|rage>90|target.time_to_die<12
+ if cast.able.execute()
+ and (not buff.suddenExecute.exists() or not buff.tasteForBlood.exists()
+ or rage() > 90 or unit.ttd(units.dyn5) < 12) and unit.hp(units.dyn5) < 20
+ then
if cast.execute() then
- ui.debug("Casting Execute [Colossus St]")
- return true
- end
- end
- -- Overpower
- -- overpower
- if cast.able.overpower() then
- if cast.overpower() then
- ui.debug("Casting Overpower [Colossus St]")
- return true
- end
- end
- -- Rend
- -- rend,if=dot.rend.remains<=gcd*5
- if cast.able.rend() and debuff.rend.remains(units.dyn5) <= unit.gcd() * 5 then
- if cast.rend() then
- ui.debug("Casting Rend [Colossus St]")
+ ui.debug("Casting Execute [Single]")
return true
end
end
-- Slam
- -- slam
- if cast.able.slam() then
+ --# Slam is preferable to overpower with crit procs/recklessness.
+ -- slam,if=target.health.pct>=20&(trinket.stacking_stat.crit.stack>=10|buff.recklessness.up)
+ if cast.able.slam() and unit.hp(units.dyn5) >= 20 and buff.recklessness.exists() then
if cast.slam() then
- ui.debug("Casting Slam [Colossus St]")
- return true
- end
- end
-end -- End Action List - ColossusSt
-
--- Action List - ColossusSweep
-actionList.ColossusSweep = function()
- -- Sweeping Strikes
- -- sweeping_strikes
- if cast.able.sweepingStrikes() then
- if cast.sweepingStrikes() then
- ui.debug("Casting Sweeping Strikes [Colossus Sweep]")
- return true
- end
- end
- -- Rend
- -- rend,if=dot.rend.remains<=gcd&buff.sweeping_strikes.up
- if cast.able.rend() and debuff.rend.remains(units.dyn5) <= unit.gcd(true) and buff.sweepingStrikes.exists() then
- if cast.rend() then
- ui.debug("Casting Rend - Sweeping Strikes [Colossus Sweep]")
- return true
- end
- end
- -- Thunderous Roar
- -- thunderous_roar
- if ui.alwaysCdAoENever("Thunderous Roar") and cast.able.thunderousRoar("player", "aoe", 1, 12) then
- if cast.thunderousRoar("player", "aoe", 1, 12) then
- ui.debug("Casting Thunderous Roar [Colossus Sweep]")
- return true
- end
- end
- -- Champions Spear
- -- champions_spear
- if ui.alwaysCdAoENever("Champion's Spear", 3, 8) and cast.able.championsSpear("target", "ground", 1, 8) then
- if cast.championsSpear("target", "ground", 1, 8) then
- ui.debug("Casting Champions Spear [Colossus Sweep]")
- return true
- end
- end
- -- Ravager
- -- ravager,if=cooldown.colossus_smash.ready
- if ui.alwaysCdAoENever("Ravager", 1, 8) and cast.able.ravager("target", "ground", 1, 8) and not cd.colossusSmash.exists() then
- if cast.ravager("target", "ground", 1, 8) then
- ui.debug("Casting Ravager [Colossus Sweep]")
- return true
- end
- end
- -- Avatar
- -- avatar
- if ui.alwaysCdAoENever("Avatar") and cast.able.avatar() then
- if cast.avatar() then
- ui.debug("Casting Avatar [Colossus Sweep]")
- return true
- end
- end
- -- Colossus Smash
- -- colossus_smash
- if ui.alwaysCdAoENever("Colossus Smash / Warbreaker", 1, 5) and cast.able.colossusSmash() then
- if cast.colossusSmash() then
- ui.debug("Casting Colossus Smash [Colossus Sweep]")
- return true
- end
- end
- -- Warbreaker
- -- warbreaker
- if ui.alwaysCdAoENever("Colossus Smash / Warbreaker", 1, 8) and cast.able.warbreaker("player", "aoe", 1, 8) then
- if cast.warbreaker("player", "aoe", 1, 8) then
- ui.debug("Casting Warbreaker [Colossus Sweep]")
- return true
- end
- end
- -- Overpower
- -- overpower,if=action.overpower.charges=2&talent.dreadnaught|buff.sweeping_strikes.up
- if cast.able.overpower() and ((charges.overpower.count() == 2 and talent.dreadnaught or buff.sweepingStrikes.exists())) then
- if cast.overpower() then
- ui.debug("Casting Overpower - Max Charges Dreadnaught / Sweeping Strikes [Colossus Sweep]")
- return true
- end
- end
- -- Mortal Strike
- -- mortal_strike,if=buff.sweeping_strikes.up
- if cast.able.mortalStrike() and buff.sweepingStrikes.exists() then
- if cast.mortalStrike() then
- ui.debug("Casting Mortal Strike - Sweeping Strikes [Colossus Sweep]")
- return true
- end
- end
- -- Skullsplitter
- -- skullsplitter,if=buff.sweeping_strikes.up
- if cast.able.skullsplitter() and buff.sweepingStrikes.exists() then
- if cast.skullsplitter() then
- ui.debug("Casting Skullsplitter [Colossus Sweep]")
- return true
- end
- end
- -- Demolish
- -- demolish,if=buff.sweeping_strikes.up&debuff.colossus_smash.up
- if ui.alwaysCdAoENever("Demolish", 3, 8) and cast.able.demolish("player", "aoe", 1, 8) and buff.sweepingStrikes.exists() and debuff.colossusSmash.exists(units.dyn5) then
- if cast.demolish("player", "aoe", 1, 8) then
- ui.debug("Casting Demolish - Sweeping Strikes [Colossus Sweep]")
- return true
- end
- end
- -- Mortal Strike
- -- mortal_strike,if=buff.sweeping_strikes.down
- if cast.able.mortalStrike() and not buff.sweepingStrikes.exists() then
- if cast.mortalStrike() then
- ui.debug("Casting Mortal Strike [Colossus Sweep]")
- return true
- end
- end
- -- Demolish
- -- demolish,if=buff.avatar.up|debuff.colossus_smash.up&cooldown.avatar.remains>=35
- if ui.alwaysCdAoENever("Demolish", 3, 8) and cast.able.demolish("player", "aoe", 1, 8) and ((buff.avatar.exists() or debuff.colossusSmash.exists(units.dyn5) and cd.avatar.remains() >= 35)) then
- if cast.demolish("player", "aoe", 1, 8) then
- ui.debug("Casting Demolish [Colossus Sweep]")
- return true
- end
- end
- -- Execute
- -- execute,if=buff.recklessness_warlords_torment.up|buff.sweeping_strikes.up
- if cast.able.execute() and ((buff.recklessness.exists() or buff.sweepingStrikes.exists())) then
- if cast.execute() then
- ui.debug("Casting Execute - Recklessness/Sweeping Strikes [Colossus Sweep]")
+ ui.debug("Casting Slam [Single]")
return true
end
end
-- Overpower
- -- overpower,if=charges=2|buff.sweeping_strikes.up
- if cast.able.overpower() and ((charges == 2 or buff.sweepingStrikes.exists())) then
+ -- overpower,if=target.health.pct>=20&rage<100|buff.sudden_execute.up
+ if cast.able.overpower() and unit.hp(units.dyn5) >= 20
+ and (rage() < 100 or buff.suddenExecute.exists())
+ then
if cast.overpower() then
- ui.debug("Casting Overpower [Colossus Sweep]")
+ ui.debug("Casting Overpower [Single]")
return true
end
end
-- Execute
-- execute
- if cast.able.execute() then
+ if cast.able.execute() and unit.hp(units.dyn5) < 20 then
if cast.execute() then
- ui.debug("Casting Execute [Colossus Sweep]")
- return true
- end
- end
- -- Thunder Clap
- -- thunder_clap,if=dot.rend.remains<=8&buff.sweeping_strikes.down
- if cast.able.thunderClap("player", "aoe", 1, 8) and debuff.rend.remains(units.dyn5) <= 8 and not buff.sweepingStrikes.exists() then
- if cast.thunderClap("player", "aoe", 1, 8) then
- ui.debug("Casting Thunder Clap [Colossus Sweep]")
+ ui.debug("Casting Execute [Single]")
return true
end
end
- -- Rend
- -- rend,if=dot.rend.remains<=5
- if cast.able.rend() and debuff.rend.remains(units.dyn5) <= 5 then
- if cast.rend() then
- ui.debug("Casting Rend [Colossus Sweep]")
- return true
- end
- end
- -- Cleave
- -- cleave,if=talent.fervor_of_battle
- if cast.able.cleave("player", "cone", 1, 8) and talent.fervorOfBattle then
- if cast.cleave("player", "cone", 1, 8) then
- ui.debug("Casting Cleave [Colossus Sweep]")
+ -- Slam
+ -- slam,if=target.health.pct>=20
+ if cast.able.slam() and unit.hp(units.dyn5) >= 20 then
+ if cast.slam() then
+ ui.debug("Casting Slam [Single]")
return true
end
end
- -- Whirlwind
- -- whirlwind,if=talent.fervor_of_battle
- if cast.able.whirlwind("player", "aoe", 1, 8) and talent.fervorOfBattle then
- if cast.whirlwind("player", "aoe", 1, 8) then
- ui.debug("Casting Whirlwind [Colossus Sweep]")
+ -- Heroic Throw
+ -- heroic_throw
+ if ui.checked("Heroic Throw") and cast.able.heroicThrow(units.dyn5) then
+ if cast.heroicThrow(units.dyn5) then
+ ui.debug("Casting Heroic Throw [Single]")
return true
end
end
- -- Slam
- -- slam
- if cast.able.slam() then
- if cast.slam() then
- ui.debug("Casting Slam [Colossus Sweep]")
+ -- Battle Shout
+ -- battle_shout
+ if cast.able.battleShout() and not buff.battleShout.exists() and rage.max() - rage() > 20 then
+ if cast.battleShout() then
+ ui.debug("Casting Battle Shout [Single]")
return true
end
end
-end -- End Action List - ColossusSweep
-
--- Action List - SlayerAoe
-actionList.SlayerAoe = function()
- -- Thunder Clap
- -- thunder_clap,if=!dot.rend.remains
- if cast.able.thunderClap("player", "aoe", 1, 8) and not debuff.rend.exists(units.dyn5) then
- if cast.thunderClap("player", "aoe", 1, 8) then
- ui.debug("Casting Thunder Clap - No Rend [Slayer Aoe]")
- return true
- end
- end
- -- Sweeping Strikes
- -- sweeping_strikes
- if cast.able.sweepingStrikes() then
- if cast.sweepingStrikes() then
- ui.debug("Casting Sweeping Strikes [Slayer Aoe]")
- return true
- end
- end
- -- Thunderous Roar
- -- thunderous_roar
- if ui.alwaysCdAoENever("Thunderous Roar") and cast.able.thunderousRoar("player", "aoe", 1, 12) then
- if cast.thunderousRoar("player", "aoe", 1, 12) then
- ui.debug("Casting Thunderous Roar [Slayer Aoe]")
- return true
- end
- end
- -- Avatar
- -- avatar
- if ui.alwaysCdAoENever("Avatar") and cast.able.avatar() then
- if cast.avatar() then
- ui.debug("Casting Avatar [Slayer Aoe]")
- return true
- end
- end
- -- Champions Spear
- -- champions_spear
- if ui.alwaysCdAoENever("Champion's Spear", 3, 8) and cast.able.championsSpear("target", "ground", 1, 8) then
- if cast.championsSpear("target", "ground", 1, 8) then
- ui.debug("Casting Champions Spear [Slayer Aoe]")
- return true
- end
- end
- -- Warbreaker
- -- warbreaker
- if ui.alwaysCdAoENever("Colossus Smash / Warbreaker", 1, 8) and cast.able.warbreaker("player", "aoe", 1, 8) then
- if cast.warbreaker("player", "aoe", 1, 8) then
- ui.debug("Casting Warbreaker [Slayer Aoe]")
- return true
- end
- end
- -- Colossus Smash
- -- colossus_smash
- if ui.alwaysCdAoENever("Colossus Smash / Warbreaker", 1, 5) and cast.able.colossusSmash() then
- if cast.colossusSmash() then
- ui.debug("Casting Colossus Smash [Slayer Aoe]")
- return true
- end
- end
- -- Cleave
- -- cleave
- if cast.able.cleave("player", "cone", 1, 8) then
- if cast.cleave("player", "cone", 1, 8) then
- ui.debug("Casting Cleave [Slayer Aoe]")
- return true
- end
- end
- -- Overpower
- -- overpower,if=buff.sweeping_strikes.up
- if cast.able.overpower() and buff.sweepingStrikes.exists() then
- if cast.overpower() then
- ui.debug("Casting Overpower - Sweeping Strikes [Slayer Aoe]")
- return true
- end
- end
- -- Execute
- -- execute,if=buff.sudden_death.up&buff.imminent_demise.stack<3
- if cast.able.execute() and buff.suddenDeath.exists() and buff.imminentDemise.stack() < 3 then
- if cast.execute() then
- ui.debug("Casting Execute - Sudden Death [Slayer Aoe]")
- return true
- end
- end
- -- Bladestorm
- -- bladestorm
- if ui.alwaysCdAoENever("Bladestorm", 3, 8) and cast.able.bladestorm("player", "aoe", 1, 8) then
- if cast.bladestorm("player", "aoe", 1, 8) then
- ui.debug("Casting Bladestorm [Slayer Aoe]")
- return true
- end
- end
- -- Skullsplitter
- -- skullsplitter,if=buff.sweeping_strikes.up
- if cast.able.skullsplitter() and buff.sweepingStrikes.exists() then
- if cast.skullsplitter() then
- ui.debug("Casting Skullsplitter [Slayer Aoe]")
- return true
- end
- end
- -- Execute
- -- execute,if=buff.sweeping_strikes.up&debuff.executioners_precision.stack<2
- if cast.able.execute() and buff.sweepingStrikes.exists() and debuff.executionersPrecision.count(units.dyn5) < 2 then
- if cast.execute() then
- ui.debug("Casting Execute - Sweeping Strikes [Slayer Aoe]")
- return true
- end
- end
- -- Mortal Strike
- -- mortal_strike,if=buff.sweeping_strikes.up&debuff.executioners_precision.stack=2
- if cast.able.mortalStrike() and buff.sweepingStrikes.exists() and debuff.executionersPrecision.count(units.dyn5) == 2 then
- if cast.mortalStrike() then
- ui.debug("Casting Mortal Strike - Executioner's Precision [Slayer Aoe]")
- return true
- end
- end
- -- Execute
- -- execute,if=debuff.marked_for_execution.up
- if cast.able.execute() and debuff.markedForExecution.exists(units.dyn5) then
- if cast.execute() then
- ui.debug("Casting Execute - Marked for Execution [Slayer Aoe]")
- return true
- end
- end
- -- Mortal Strike
- -- mortal_strike,if=buff.sweeping_strikes.up
- if cast.able.mortalStrike() and buff.sweepingStrikes.exists() then
- if cast.mortalStrike() then
- ui.debug("Casting Mortal Strike - Sweeping Strikes [Slayer Aoe]")
- return true
- end
- end
- -- Overpower
- -- overpower,if=talent.dreadnaught
- if cast.able.overpower() and talent.dreadnaught then
- if cast.overpower() then
- ui.debug("Casting Overpower - Dreadnaught [Slayer Aoe]")
- return true
- end
- end
- -- Thunder Clap
- -- thunder_clap
- if cast.able.thunderClap("player", "aoe", 1, 8) then
- if cast.thunderClap("player", "aoe", 1, 8) then
- ui.debug("Casting Thunder Clap [Slayer Aoe]")
- return true
- end
- end
- -- Overpower
- -- overpower
- if cast.able.overpower() then
- if cast.overpower() then
- ui.debug("Casting Overpower [Slayer Aoe]")
- return true
- end
- end
- -- Execute
- -- execute
- if cast.able.execute() then
- if cast.execute() then
- ui.debug("Casting Execute [Slayer Aoe]")
- return true
- end
- end
- -- Mortal Strike
- -- mortal_strike
- if cast.able.mortalStrike() then
- if cast.mortalStrike() then
- ui.debug("Casting Mortal Strike [Slayer Aoe]")
- return true
- end
- end
- -- Whirlwind
- -- whirlwind
- if cast.able.whirlwind("player", "aoe", 1, 8) then
- if cast.whirlwind("player", "aoe", 1, 8) then
- ui.debug("Casting Whirlwind [Slayer Aoe]")
- return true
- end
- end
- -- Skullsplitter
- -- skullsplitter
- if cast.able.skullsplitter() then
- if cast.skullsplitter() then
- ui.debug("Casting Skullsplitter [Slayer Aoe]")
- return true
- end
- end
- -- Slam
- -- slam
- if cast.able.slam() then
- if cast.slam() then
- ui.debug("Casting Slam [Slayer Aoe]")
- return true
- end
- end
- -- Storm Bolt
- -- storm_bolt,if=buff.bladestorm.up
- if cast.able.stormBolt() and buff.bladestorm.exists() then
- if cast.stormBolt() then
- ui.debug("Casting Storm Bolt [Slayer Aoe]")
- return true
- end
- end
-end -- End Action List - SlayerAoe
-
--- Action List - SlayerSt
-actionList.SlayerSt = function()
- -- Rend
- -- rend,if=dot.rend.remains<=gcd
- if cast.able.rend() and debuff.rend.remains(units.dyn5) <= unit.gcd(true) then
- if cast.rend() then
- ui.debug("Casting Rend - Reapply Now [Slayer St]")
- return true
- end
- end
- -- Thunderous Roar
- -- thunderous_roar
- if ui.alwaysCdAoENever("Thunderous Roar") and cast.able.thunderousRoar("player", "aoe", 1, 12) then
- if cast.thunderousRoar("player", "aoe", 1, 8) then
- ui.debug("Casting Thunderous Roar [Slayer St]")
- return true
- end
- end
- -- Champions Spear
- -- champions_spear
- if ui.alwaysCdAoENever("Champion's Spear", 3, 8) and cast.able.championsSpear("target", "ground", 1, 8) then
- if cast.championsSpear("target", "ground", 1, 8) then
- ui.debug("Casting Champions Spear [Slayer St]")
- return true
- end
- end
- -- Avatar
- -- avatar,if=cooldown.colossus_smash.remains<=5|debuff.colossus_smash.up
- if ui.alwaysCdAoENever("Avatar") and cast.able.avatar() and ((cd.colossusSmash.remains() <= 5 or debuff.colossusSmash.exists(units.dyn5))) then
- if cast.avatar() then
- ui.debug("Casting Avatar [Slayer St]")
- return true
- end
- end
- -- Colossus Smash
- -- colossus_smash
- if ui.alwaysCdAoENever("Colossus Smash / Warbreaker", 1, 5) and cast.able.colossusSmash() then
- if cast.colossusSmash() then
- ui.debug("Casting Colossus Smash [Slayer St]")
- return true
- end
- end
- -- Warbreaker
- -- warbreaker
- if ui.alwaysCdAoENever("Colossus Smash / Warbreaker", 1, 8) and cast.able.warbreaker("player", "aoe", 1, 8) then
- if cast.warbreaker("player", "aoe", 1, 8) then
- ui.debug("Casting Warbreaker [Slayer St]")
- return true
- end
- end
- -- Execute
- -- execute,if=debuff.marked_for_execution.stack=3|buff.juggernaut.remains<=gcd*3|buff.sudden_death.stack=2
- if cast.able.execute() and ((debuff.markedForExecution.count(units.dyn5) == 3 or (buff.juggernaut.exists() and buff.juggernaut.remains() <= unit.gcd(true) * 3) or buff.suddenDeath.stack() == 2)) then
- if cast.execute() then
- ui.debug("Casting Execute [Slayer St]")
- return true
- end
- end
- -- Bladestorm
- -- bladestorm,if=cooldown.colossus_smash.remains>=gcd*4|buff.colossus_smash.remains>=gcd*4
- if ui.alwaysCdAoENever("Bladestorm", 3, 8) and cast.able.bladestorm("player", "aoe", 1, 8) and ((cd.colossusSmash.remains() >= unit.gcd(true) * 4 or debuff.colossusSmash.remains(units.dyn5) >= unit.gcd(true) * 4)) then
- if cast.bladestorm("player", "aoe", 1, 8) then
- ui.debug("Casting Bladestorm [Slayer St]")
- return true
- end
- end
- -- Overpower
- -- overpower,if=buff.opportunist.up
- if cast.able.overpower() and buff.opportunist.exists() then
- if cast.overpower() then
- ui.debug("Casting Overpower - Opportunist [Slayer St]")
- return true
- end
- end
- -- Mortal Strike
- -- mortal_strike
- if cast.able.mortalStrike() then
- if cast.mortalStrike() then
- ui.debug("Casting Mortal Strike [Slayer St]")
- return true
- end
- end
- -- Skullsplitter
- -- skullsplitter
- if cast.able.skullsplitter() then
- if cast.skullsplitter() then
- ui.debug("Casting Skullsplitter [Slayer St]")
- return true
- end
- end
- -- Overpower
- -- overpower
- if cast.able.overpower() then
- if cast.overpower() then
- ui.debug("Casting Overpower [Slayer St]")
- return true
- end
- end
- -- Rend
- -- rend,if=dot.rend.remains<=gcd*5
- if cast.able.rend() and debuff.rend.remains(units.dyn5) <= unit.gcd(true) * 5 then
- if cast.rend() then
- ui.debug("Casting Rend [Slayer St]")
- return true
- end
- end
- -- Cleave
- -- cleave,if=buff.martial_prowess.down
- if cast.able.cleave("player", "cone", 1, 8) and not buff.overpower.exists() then
- if cast.cleave("player", "cone", 1, 8) then
- ui.debug("Casting Cleave [Slayer St]")
- return true
- end
- end
- -- Slam
- -- slam
- if cast.able.slam() then
- if cast.slam() then
- ui.debug("Casting Slam [Slayer St]")
- return true
- end
- end
- -- Storm Bolt
- -- storm_bolt,if=buff.bladestorm.up
- if cast.able.stormBolt() and buff.bladestorm.exists() then
- if cast.stormBolt() then
- ui.debug("Casting Storm Bolt [Slayer St]")
- return true
- end
- end
-end -- End Action List - SlayerSt
-
--- Action List - SlayerExecute
-actionList.SlayerExecute = function()
- -- Sweeping Strikes
- -- sweeping_strikes,if=active_enemies=2
- if cast.able.sweepingStrikes() and #enemies.yards5f == 2 then
- if cast.sweepingStrikes() then
- ui.debug("Casting Sweeping Strikes [Slayer Execute]")
- return true
- end
- end
- -- Rend
- -- rend,if=dot.rend.remains<=gcd&!talent.bloodletting
- if cast.able.rend(var.minHpUnit) and debuff.rend.remains(var.minHpUnit) <= unit.gcd(true) and not talent.bloodletting then
- if cast.rend(var.minHpUnit) then
- ui.debug("Casting Rend [Slayer Execute]")
- return true
- end
- end
- -- Thunderous Roar
- -- thunderous_roar
- if ui.alwaysCdAoENever("Thunderous Roar") and cast.able.thunderousRoar("player", "aoe", 1, 12) then
- if cast.thunderousRoar("player", "aoe", 1, 12) then
- ui.debug("Casting Thunderous Roar [Slayer Execute]")
- return true
- end
- end
- -- Champions Spear
- -- champions_spear
- if ui.alwaysCdAoENever("Champion's Spear", 3, 8) and cast.able.championsSpear(var.minHpUnit, "ground", 1, 8) then
- if cast.championsSpear(var.minHpUnit, "ground", 1, 8) then
- ui.debug("Casting Champions Spear [Slayer Execute]")
- return true
- end
- end
- -- Avatar
- -- avatar,if=cooldown.colossus_smash.remains<=5|debuff.colossus_smash.up
- if ui.alwaysCdAoENever("Avatar") and cast.able.avatar() and ((cd.colossusSmash.remains() <= 5 or debuff.colossusSmash.exists(var.minHpUnit))) then
- if cast.avatar() then
- ui.debug("Casting Avatar [Slayer Execute]")
- return true
- end
- end
- -- Warbreaker
- -- warbreaker
- if ui.alwaysCdAoENever("Colossus Smash / Warbreaker", 1, 8) and cast.able.warbreaker("player", "aoe", 1, 8) then
- if cast.warbreaker("player", "aoe", 1, 8) then
- ui.debug("Casting Warbreaker [Slayer Execute]")
- return true
- end
- end
- -- Colossus Smash
- -- colossus_smash
- if ui.alwaysCdAoENever("Colossus Smash / Warbreaker", 1, 5) and cast.able.colossusSmash(var.minHpUnit) then
- if cast.colossusSmash(var.minHpUnit) then
- ui.debug("Casting Colossus Smash [Slayer Execute]")
- return true
- end
- end
- -- Execute
- -- execute,if=buff.juggernaut.remains<=gcd
- if cast.able.execute(var.minHpUnit) and buff.juggernaut.remains() <= unit.gcd(true) then
- if cast.execute(var.minHpUnit) then
- ui.debug("Casting Execute - Juggernaut [Slayer Execute]")
- return true
- end
- end
- -- Bladestorm
- -- bladestorm,if=debuff.executioners_precision.stack=2&debuff.colossus_smash.remains>4|debuff.executioners_precision.stack=2&cooldown.colossus_smash.remains>15|!talent.executioners_precision
- if ui.alwaysCdAoENever("Bladestorm", 3, 8) and cast.able.bladestorm("player", "aoe", 1, 8) and ((debuff.executionersPrecision.count(units.dyn5) == 2
- and debuff.colossusSmash.remains(units.dyn5) > 4 or debuff.executionersPrecision.count(units.dyn5) == 2
- and cd.colossusSmash.remains() > 15 or not talent.executionersPrecision))
- then
- if cast.bladestorm("player", "aoe", 1, 8) then
- ui.debug("Casting Bladestorm [Slayer Execute]")
- return true
- end
- end
- -- Skullsplitter
- -- skullsplitter,if=rage<85
- if cast.able.skullsplitter(var.minHpUnit) and rage() < 85 then
- if cast.skullsplitter(var.minHpUnit) then
- ui.debug("Casting Skullsplitter [Slayer Execute]")
- return true
- end
- end
- -- Mortal Strike
- -- mortal_strike,if=dot.rend.remains<2|(debuff.executioners_precision.stack=2&buff.lethal_blows.stack=2)
- if cast.able.mortalStrike(var.minHpUnit) and ((debuff.rend.remains(var.minHpUnit) < 2 or (debuff.executionersPrecision.count(var.minHpUnit) == 2
- and buff.lethalBlows.stack() == 2)))
- then
- if cast.mortalStrike(var.minHpUnit) then
- ui.debug("Casting Mortal Strike - No Rend Soon / Lethal Blows [Slayer Execute]")
- return true
- end
- end
- -- Overpower
- -- overpower,if=buff.opportunist.up&rage<80&buff.martial_prowess.stack<2
- if cast.able.overpower(var.minHpUnit) and buff.opportunist.exists() and rage() < 80 and buff.overpower.stack() < 2 then
- if cast.overpower(var.minHpUnit) then
- ui.debug("Casting Overpower - Opportunist [Slayer Execute]")
- return true
- end
- end
- -- Execute
- -- execute
- if cast.able.execute(var.minHpUnit) then
- if cast.execute(var.minHpUnit) then
- ui.debug("Casting Execute [Slayer Execute]")
- return true
- end
- end
- -- Overpower
- -- overpower
- if cast.able.overpower(var.minHpUnit) then
- if cast.overpower(var.minHpUnit) then
- ui.debug("Casting Overpower [Slayer Execute]")
- return true
- end
- end
- -- Mortal Strike
- -- mortal_strike,if=!talent.executioners_precision
- if cast.able.mortalStrike(var.minHpUnit) and not talent.executionersPrecision then
- if cast.mortalStrike(var.minHpUnit) then
- ui.debug("Casting Mortal Strike [Slayer Execute]")
- return true
- end
- end
- -- Storm Bolt
- -- storm_bolt,if=buff.bladestorm.up
- if cast.able.stormBolt(var.minHpUnit) and buff.bladestorm.exists() then
- if cast.stormBolt(var.minHpUnit) then
- ui.debug("Casting Storm Bolt [Slayer Execute]")
- return true
- end
- end
-end -- End Action List - SlayerExecute
-
--- Action List - SlayerSweep
-actionList.SlayerSweep = function()
- -- Thunderous Roar
- -- thunderous_roar
- if ui.alwaysCdAoENever("Thunderous Roar") and cast.able.thunderousRoar("player", "aoe", 1, 12) then
- if cast.thunderousRoar("player", "aoe", 1, 12) then
- ui.debug("Casting Thunderous Roar [Slayer Sweep]")
- return true
- end
- end
- -- Sweeping Strikes
- -- sweeping_strikes
- if cast.able.sweepingStrikes() then
- if cast.sweepingStrikes() then
- ui.debug("Casting Sweeping Strikes [Slayer Sweep]")
- return true
- end
- end
- -- Rend
- -- rend,if=dot.rend.remains<=gcd
- if cast.able.rend() and debuff.rend.remains(units.dyn5) <= unit.gcd() then
- if cast.rend() then
- ui.debug("Casting Rend - Reapply Now [Slayer Sweep]")
- return true
- end
- end
- -- Champions Spear
- -- champions_spear
- if ui.alwaysCdAoENever("Champion's Spear", 3, 8) and cast.able.championsSpear("target", "ground", 1, 8) then
- if cast.championsSpear("target", "ground", 1, 8) then
- ui.debug("Casting Champions Spear [Slayer Sweep]")
- return true
- end
- end
- -- Avatar
- -- avatar
- if ui.alwaysCdAoENever("Avatar") and cast.able.avatar() then
- if cast.avatar() then
- ui.debug("Casting Avatar [Slayer Sweep]")
- return true
- end
- end
- -- Colossus Smash
- -- colossus_smash
- if ui.alwaysCdAoENever("Colossus Smash / Warbreaker", 1, 5) and cast.able.colossusSmash() then
- if cast.colossusSmash() then
- ui.debug("Casting Colossus Smash [Slayer Sweep]")
- return true
- end
- end
- -- Warbreaker
- -- warbreaker
- if ui.alwaysCdAoENever("Colossus Smash / Warbreaker", 1, 8) and cast.able.warbreaker("player", "aoe", 1, 8) then
- if cast.warbreaker("player", "aoe", 1, 8) then
- ui.debug("Casting Warbreaker [Slayer Sweep]")
- return true
- end
- end
- -- Skullsplitter
- -- skullsplitter,if=buff.sweeping_strikes.up
- if cast.able.skullsplitter() and buff.sweepingStrikes.exists() then
- if cast.skullsplitter() then
- ui.debug("Casting Skullsplitter [Slayer Sweep]")
- return true
- end
- end
- -- Execute
- -- execute,if=debuff.marked_for_execution.stack=3
- if cast.able.execute() and debuff.markedForExecution.count(units.dyn5) == 3 then
- if cast.execute() then
- ui.debug("Casting Execute - Marked For Execution [Slayer Sweep]")
- return true
- end
- end
- -- Bladestorm
- -- bladestorm
- if ui.alwaysCdAoENever("Bladestorm", 3, 8) and cast.able.bladestorm("player", "aoe", 1, 8) then
- if cast.bladestorm("player", "aoe", 1, 8) then
- ui.debug("Casting Bladestorm [Slayer Sweep]")
- return true
- end
- end
- -- Overpower
- -- overpower,if=talent.dreadnaught|buff.opportunist.up
- if cast.able.overpower() and ((talent.dreadnaught or buff.opportunist.exists())) then
- if cast.overpower() then
- ui.debug("Casting Overpower - Dreadnaught / Opportunist [Slayer Sweep]")
- return true
- end
- end
- -- Mortal Strike
- -- mortal_strike
- if cast.able.mortalStrike() then
- if cast.mortalStrike() then
- ui.debug("Casting Mortal Strike [Slayer Sweep]")
- return true
- end
- end
- -- Cleave
- -- cleave,if=talent.fervor_of_battle
- if cast.able.cleave("player", "cone", 1, 8) and talent.fervorOfBattle then
- if cast.cleave("player", "cone", 1, 8) then
- ui.debug("Casting Cleave [Slayer Sweep]")
- return true
- end
- end
- -- Execute
- -- execute
- if cast.able.execute() then
- if cast.execute() then
- ui.debug("Casting Execute [Slayer Sweep]")
- return true
- end
- end
- -- Overpower
- -- overpower
- if cast.able.overpower() then
- if cast.overpower() then
- ui.debug("Casting Overpower [Slayer Sweep]")
- return true
- end
- end
- -- Thunder Clap
- -- thunder_clap,if=dot.rend.remains<=8&buff.sweeping_strikes.down
- if cast.able.thunderClap("player", "aoe", 1, 8) and debuff.rend.remains(units.dyn5) <= 8 and not buff.sweepingStrikes.exists() then
- if cast.thunderClap("player", "aoe", 1, 8) then
- ui.debug("Casting Thunder Clap [Slayer Sweep]")
- return true
- end
- end
- -- Rend
- -- rend,if=dot.rend.remains<=5
- if cast.able.rend() and debuff.rend.remains(units.dyn5) <= 5 then
- if cast.rend() then
- ui.debug("Casting Rend [Slayer Sweep]")
- return true
- end
- end
- -- Whirlwind
- -- whirlwind,if=talent.fervor_of_battle
- if cast.able.whirlwind("player", "aoe", 1, 8) and talent.fervorOfBattle then
- if cast.whirlwind("player", "aoe", 1, 8) then
- ui.debug("Casting Whirlwind [Slayer Sweep]")
- return true
- end
- end
- -- Slam
- -- slam
- if cast.able.slam() then
- if cast.slam() then
- ui.debug("Casting Slam [Slayer Sweep]")
- return true
- end
- end
- -- Storm Bolt
- -- storm_bolt,if=buff.bladestorm.up
- if cast.able.stormBolt() and buff.bladestorm.exists() then
- if cast.stormBolt() then
- ui.debug("Casting Storm Bolt [Slayer Sweep]")
- return true
- end
- end
-end -- End Action List - SlayerSweep
-
--- Action List - Trinkets
-actionList.Trinkets = function()
- -- -- Do Treacherous Transmitter Task
- -- -- do_treacherous_transmitter_task
- -- if cast.able.doTreacherousTransmitterTask() then
- -- if cast.doTreacherousTransmitterTask() then ui.debug("Casting Do Treacherous Transmitter Task [Trinkets]") return true end
- -- end
-
- -- -- Use Item - Treacherous Transmitter
- -- -- use_item,name=treacherous_transmitter,if=(variable.adds_remain|variable.st_planning)&cooldown.avatar.remains<3
- -- if use.able.treacherousTransmitter() and (((var.addsRemain or var.stPlanning) and cd.avatar.remains()<3)) then
- -- if use.treacherousTransmitter() then ui.debug("Using Treacherous Transmitter [Trinkets]") return true end
- -- end
-
- -- -- Use Item - Slot1
- -- -- use_item,slot=trinket1,if=variable.trinket_1_buffs&!variable.trinket_1_manual&(!buff.avatar.up&trinket.1.cast_time>0|!trinket.1.cast_time>0)&buff.avatar.up&(variable.trinket_2_exclude|!trinket.2.has_cooldown|trinket.2.cooldown.remains|variable.trinket_priority=1)|trinket.1.proc.any_dps.duration>=fight_remains
- -- -- TODO: The following conditions were not converted:
- -- -- trinket.1.cast_time
- -- -- trinket.1.cast_time
- -- -- trinket.2.has_cooldown
- -- -- trinket.1.proc.any_dps.duration
- -- if use.able.slot1() and ((var.trinket1Buffs and not var.trinket1Manual and (not buff.avatar.exists() and >0 or not >0) and buff.avatar.exists() and (var.trinket2Exclude or not or cd.slot.remains(14) or var.trinketPriority==1) or >=unit.ttdGroup(40))) then
- -- if use.slot1() then ui.debug("Using Slot1 [Trinkets]") return true end
- -- end
-
- -- -- Use Item - Slot2
- -- -- use_item,slot=trinket2,if=variable.trinket_2_buffs&!variable.trinket_2_manual&(!buff.avatar.up&trinket.2.cast_time>0|!trinket.2.cast_time>0)&buff.avatar.up&(variable.trinket_1_exclude|!trinket.1.has_cooldown|trinket.1.cooldown.remains|variable.trinket_priority=2)|trinket.2.proc.any_dps.duration>=fight_remains
- -- -- TODO: The following conditions were not converted:
- -- -- trinket.2.cast_time
- -- -- trinket.2.cast_time
- -- -- trinket.1.has_cooldown
- -- -- trinket.2.proc.any_dps.duration
- -- if use.able.slot2() and ((var.trinket2Buffs and not var.trinket2Manual and (not buff.avatar.exists() and >0 or not >0) and buff.avatar.exists() and (var.trinket1Exclude or not or cd.slot.remains(13) or var.trinketPriority==2) or >=unit.ttdGroup(40))) then
- -- if use.slot2() then ui.debug("Using Slot2 [Trinkets]") return true end
- -- end
-
- -- -- Use Item - Slot1
- -- -- use_item,slot=trinket1,if=!variable.trinket_1_buffs&(trinket.1.cast_time>0&!buff.avatar.up|!trinket.1.cast_time>0)&!variable.trinket_1_manual&(!variable.trinket_1_buffs&(trinket.2.cooldown.remains|!variable.trinket_2_buffs)|(trinket.1.cast_time>0&!buff.avatar.up|!trinket.1.cast_time>0)|cooldown.avatar.remains_expected>20)
- -- -- TODO: The following conditions were not converted:
- -- -- trinket.1.cast_time
- -- -- trinket.1.cast_time
- -- -- trinket.1.cast_time
- -- -- trinket.1.cast_time
- -- -- cooldown.avatar.remains_expected
- -- if use.able.slot1() and ((not var.trinket1Buffs and (>0 and not buff.avatar.exists() or not >0) and not var.trinket1Manual and (not var.trinket1Buffs and (cd.slot.remains(14) or not var.trinket2Buffs) or (>0 and not buff.avatar.exists() or not >0) or >20))) then
- -- if use.slot1() then ui.debug("Using Slot1 [Trinkets]") return true end
- -- end
-
- -- -- Use Item - Slot2
- -- -- use_item,slot=trinket2,if=!variable.trinket_2_buffs&(trinket.2.cast_time>0&!buff.avatar.up|!trinket.2.cast_time>0)&!variable.trinket_2_manual&(!variable.trinket_2_buffs&(trinket.1.cooldown.remains|!variable.trinket_1_buffs)|(trinket.2.cast_time>0&!buff.avatar.up|!trinket.2.cast_time>0)|cooldown.avatar.remains_expected>20)
- -- -- TODO: The following conditions were not converted:
- -- -- trinket.2.cast_time
- -- -- trinket.2.cast_time
- -- -- trinket.2.cast_time
- -- -- trinket.2.cast_time
- -- -- cooldown.avatar.remains_expected
- -- if use.able.slot2() and ((not var.trinket2Buffs and (>0 and not buff.avatar.exists() or not >0) and not var.trinket2Manual and (not var.trinket2Buffs and (cd.slot.remains(13) or not var.trinket1Buffs) or (>0 and not buff.avatar.exists() or not >0) or >20))) then
- -- if use.slot2() then ui.debug("Using Slot2 [Trinkets]") return true end
- -- end
-
- -- -- Use Item - Slot=Main Hand
- -- -- use_item,slot=main_hand,if=!equipped.fyralath_the_dreamrender&(!variable.trinket_1_buffs|trinket.1.cooldown.remains)&(!variable.trinket_2_buffs|trinket.2.cooldown.remains)
- -- if use.able.slot=mainHand() and ((not equiped.fyralathTheDreamrender() and (not var.trinket1Buffs or cd.slot.remains(13)) and (not var.trinket2Buffs or cd.slot.remains(14)))) then
- -- if use.slot=mainHand() then ui.debug("Using Slot=Main Hand [Trinkets]") return true end
- -- end
- module.BasicTrinkets()
-end -- End Action List - Trinkets
-
--- Action List - Variables
-actionList.Variables = function()
- -- Variable - St Planning
- -- variable,name=st_planning,value=active_enemies=1&(raid_event.adds.in>15|!raid_event.adds.exists)
- var.stPlanning = (#enemies.yards5f == 1)
-
- -- Variable - Adds Remain
- -- variable,name=adds_remain,value=active_enemies>=2&(!raid_event.adds.exists|raid_event.adds.exists&raid_event.adds.remains>5)
- var.addsRemain = (#enemies.yards5f >= 2)
-
- -- Variable - Execute Phase
- -- variable,name=execute_phase,value=(talent.massacre.enabled&target.health.pct<35)|target.health.pct<20
- var.executePhase = ((talent.massacre and unit.hp(units.dyn5) < 35) or unit.hp(units.dyn5) < 20)
-end -- End Action List - Variables
+end -- End Action List - Single
-- Action List - PreCombat
actionList.PreCombat = function()
@@ -1715,49 +655,22 @@ actionList.PreCombat = function()
-- Module - Imbue Up
-- augmentation
module.ImbueUp()
- -- Variable - Trinket 1 Exclude
- -- variable,name=trinket_1_exclude,value=trinket.1.is.treacherous_transmitter
- var.trinket1Exclude = false -- TODO: Optional replace with UI option, review SimC for detailed notes
- -- Variable - Trinket 2 Exclude
- -- variable,name=trinket_2_exclude,value=trinket.2.is.treacherous_transmitter
- var.trinket2Exclude = false -- TODO: Optional replace with UI option, review SimC for detailed notes
- -- Variable - Trinket 1 Sync,Op=Setif,Value=1,Value Else=0.5,Condition=Trinket.1.Has Use Buff&(Trinket.1.Cooldown.Duration%%Cooldown.Avatar.Duration=0)
- -- variable,name=trinket_1_sync,op=setif,value=1,value_else=0.5,condition=trinket.1.has_use_buff&(trinket.1.cooldown.duration%%cooldown.avatar.duration=0)
- var.trinket1Sync = false
- -- Variable - Trinket 2 Sync,Op=Setif,Value=1,Value Else=0.5,Condition=Trinket.2.Has Use Buff&(Trinket.2.Cooldown.Duration%%Cooldown.Avatar.Duration=0)
- -- variable,name=trinket_2_sync,op=setif,value=1,value_else=0.5,condition=trinket.2.has_use_buff&(trinket.2.cooldown.duration%%cooldown.avatar.duration=0)
- var.trinket2Sync = false
- -- Variable - Trinket 1 Buffs
- -- variable,name=trinket_1_buffs,value=trinket.1.has_use_buff|(trinket.1.has_stat.any_dps&!variable.trinket_1_exclude)
- var.trinket1Buffs = false
- -- Variable - Trinket 2 Buffs
- -- variable,name=trinket_2_buffs,value=trinket.2.has_use_buff|(trinket.2.has_stat.any_dps&!variable.trinket_2_exclude)
- var.trinket2Buffs = false
- -- Variable - Trinket Priority,Op=Setif,Value=2,Value Else=1,Condition=!Variable.Trinket 1 Buffs&Variable.Trinket 2 Buffs|Variable.Trinket 2 Buffs&((Trinket.2.Cooldown.Duration%Trinket.2.Proc.Any Dps.Duration)*(1.5+Trinket.2.Has Buff.Strength)*(Variable.Trinket 2 Sync))>((Trinket.1.Cooldown.Duration%Trinket.1.Proc.Any Dps.Duration)*(1.5+Trinket.1.Has Buff.Strength)*(Variable.Trinket 1 Sync))
- -- variable,name=trinket_priority,op=setif,value=2,value_else=1,condition=!variable.trinket_1_buffs&variable.trinket_2_buffs|variable.trinket_2_buffs&((trinket.2.cooldown.duration%trinket.2.proc.any_dps.duration)*(1.5+trinket.2.has_buff.strength)*(variable.trinket_2_sync))>((trinket.1.cooldown.duration%trinket.1.proc.any_dps.duration)*(1.5+trinket.1.has_buff.strength)*(variable.trinket_1_sync))
- var.trinketPriority = 1
- -- Variable - Trinket 1 Manual
- -- variable,name=trinket_1_manual,value=trinket.1.is.algethar_puzzle_box
- var.trinket1Manual = false -- TODO: Optional replace with UI option, review SimC for detailed notes
- -- Variable - Trinket 2 Manual
- -- variable,name=trinket_2_manual,value=trinket.2.is.algethar_puzzle_box
- var.trinket2Manual = false -- TODO: Optional replace with UI option, review SimC for detailed notes
-- Pre-Pull
if ui.checked("Pre-Pull Timer") and ui.pullTimer() <= ui.value("Pre-Pull Timer") then
end -- End Pre-Pull
+ -- -- Battle Stance
+ -- -- battle_stance,toggle=on
+ -- if cast.able.battleStance() and unit.form() ~= 1 then
+ -- if cast.battleStance() then
+ -- ui.debug("Casting Battle Stance [Precombat]")
+ -- return true
+ -- end
+ -- end
if unit.valid("target") then
- -- Battle Stance
- -- battle_stance,toggle=on
- if cast.able.battleStance() and not buff.battleStance.exists() then
- if cast.battleStance() then
- ui.debug("Casting Battle Stance [Precombat]")
- return true
- end
- end
-- Charge
-- charge
- if ui.mode.mover == 1 and ui.checked("Charge") and cast.able.charge("target") then
+ if ui.mode.mover == 1 and ui.checked("Charge") and cast.able.charge("target") and unit.distance("target") > 8 then
if cast.charge("target") then
ui.debug("Casting Charge [Precombat]")
return true
@@ -1772,6 +685,14 @@ actionList.PreCombat = function()
return true
end
end
+ -- Shattering Throw
+ -- shattering_throw
+ if ui.alwaysCdNever("Shattering Throw") and cast.able.shatteringThrow("target") then
+ if cast.shatteringThrow("target") then
+ ui.debug("Casting Shattering Throw [Precombat]")
+ return true
+ end
+ end
-- Start Attack
if unit.distance("target") < 5 then
if cast.able.autoAttack("target") then
@@ -1799,130 +720,143 @@ actionList.Combat = function()
if unit.distance("target") <= 5 then
if cast.able.autoAttack("target") then
if cast.autoAttack("target") then
- ui.debug("Casting Auto Attack")
+ ui.debug("Casting Auto Attack [Combat]")
return true
end
end
end
-- Charge
-- charge
- if ui.mode.mover == 1 and ui.checked("Charge") and cast.able.charge(units.dyn5) then
+ if ui.mode.mover == 1 and ui.checked("Charge") and cast.able.charge(units.dyn5) and unit.distance(units.dyn5) > 8 then
if cast.charge(units.dyn5) then
- ui.debug("Casting Charge")
+ ui.debug("Casting Charge [Combat]")
return true
end
end
-- Module - Combatpotion Up
- -- potion,if=gcd.remains=0&debuff.colossus_smash.remains>8|target.time_to_die<25
- if unit.gcd() == 0 and debuff.colossusSmash.remain() > 8 or unit.ttd(units.dyn5) < 25 then
+ -- mogu_power_potion,if=(target.health.pct<20&buff.recklessness.up)|buff.bloodlust.react|target.time_to_die<=25
+ if (unit.hp(units.dyn5) < 20 and buff.recklessness.exists()) or buff.bloodLust.exists() or unit.ttd(units.dyn5) < 25 then
module.CombatPotionUp()
end
+ -- Shattering Throw
+ -- shattering_throw
+ if ui.alwaysCdNever("Shattering Throw") and cast.able.shatteringThrow(units.dyn5) then
+ if cast.shatteringThrow(units.dyn5) then
+ ui.debug("Casting Shattering Throw [Combat]")
+ return true
+ end
+ end
-- Action List - Interrupts
if actionList.Interrupts() then return true end
- -- Call Action List - Variables
- -- call_action_list,name=variables
- if actionList.Variables() then return true end
- -- Call Action List - Trinkets
- -- call_action_list,name=trinkets
- if actionList.Trinkets() then return true end
- -- Racials
- if ui.alwaysCdNever("Racial") and cast.able.racial() then
- -- Arcane Torrent (Blood Elf)
- -- arcane_torrent,if=cooldown.mortal_strike.remains>1.5&rage<50
- if race == "BloodElf" and cast.able.arcaneTorrent() and cd.mortalStrike.remains() > 1.5 and rage() < 50 then
- if cast.arcaneTorrent() then
- ui.debug("Casting Arcane Torrent")
- return true
- end
- end
- -- Light's Judgment (Lightforged Draenei)
- -- lights_judgment,if=debuff.colossus_smash.down&cooldown.mortal_strike.remains
- if race == "LightforgedDraenei" and cast.able.lightsJudgment() and not debuff.colossusSmash.exists(units.dyn5) and cd.mortalStrike.exists() then
- if cast.lightsJudgment() then
- ui.debug("Casting Light's Judgement")
- return true
- end
+ -- Recklessness
+ -- recklessness,if=!talent.bloodbath.enabled&((cooldown.colossus_smash.remains<2|debuff.colossus_smash.remains>=5)&(target.time_to_die>(192*buff.cooldown_reduction.value)|target.health.pct<20))|buff.bloodbath.up&(target.time_to_die>(192*buff.cooldown_reduction.value)|target.health.pct<20)|target.time_to_die<=12
+ if ui.alwaysCdAoENever("Recklessness",3,#enemies.yards8) and cast.able.recklessness() and (not talent.bloodbath and ((cd.colossusSmash.remains() < 2 or debuff.colossusSmash.remains(units.dyn5) >= 5 or not var.knownCS)
+ and (unit.ttd(units.dyn5) > (192 * 1--[[buff.cooldownReduction.value()]]) or unit.hp(units.dyn5) < 20)) or buff.bloodbath.exists()
+ and (unit.ttd(units.dyn5) > (192 * 1--[[buff.cooldownReduction.value()]]) or unit.hp(units.dyn5) < 20) or unit.ttd(units.dyn5) <= 12)
+ then
+ if cast.recklessness() then
+ ui.debug("Casting Recklessness [Combat]")
+ return true
end
- -- Bag Of Tricks
- -- bag_of_tricks,if=debuff.colossus_smash.down&cooldown.mortal_strike.remains
- if race == "Vulpera" and cast.able.bagOfTricks() and not debuff.colossusSmash.exists(units.dyn5) and cd.mortalStrike.remains() then
- if cast.bagOfTricks() then
- ui.debug("Casting Bag Of Tricks [Combat]")
- return true
- end
+ end
+ -- Avatar
+ -- avatar,if=enabled&(buff.recklessness.up|target.time_to_die<=25)
+ if ui.alwaysCdNever("Avatar") and cast.able.avatar() and (buff.recklessness.exists() or unit.ttd(units.dyn5) <= 25) then
+ if cast.avatar() then
+ ui.debug("Casting Avatar [Combat]")
+ return true
end
- -- Berserking (Troll)
- -- berserking,if=debuff.colossus_smash.remains>6
- if race == "Troll" and cast.able.berserking() and debuff.colossusSmash.remain(units.dyn5) > 6 then
- if cast.berserking() then
- ui.debug("Casting Berserking")
- return true
- end
+ end
+ -- Skull Banner
+ -- skull_banner,if=buff.skull_banner.down&(((cooldown.colossus_smash.remains<2|debuff.colossus_smash.remains>=5)&target.time_to_die>192&buff.cooldown_reduction.up)|buff.recklessness.up)
+ if ui.alwaysCdNever("Skull Banner") and cast.able.skullBanner() and not buff.skullBanner.exists()
+ and (((cd.colossusSmash.remains() < 2 or debuff.colossusSmash.remains(units.dyn5) >= 5)
+ and unit.ttd(units.dyn5) > 192 --[[and buff.cooldownReduction.exists()]]) or buff.recklessness.exists())
+ then
+ if cast.skullBanner() then
+ ui.debug("Casting Skull Banner [Combat]")
+ return true
end
+ end
+ -- Use Item: Hands
+ -- use_item,slot=hands,if=!talent.bloodbath.enabled&debuff.colossus_smash.up|buff.bloodbath.up
+ -- if use.able.item.slot(9) and ((not talent.bloodbath and debuff.colossusSmash.exists(units.dyn5)) or buff.bloodbath.exists()) then
+ -- if use.item.slot(9) then
+ -- ui.debug("Using Item: Hands")
+ -- return true
+ -- end
+ -- end
+ -- Racials
+ if ui.alwaysCdNever("Racial") and cast.able.racial() then
-- Blood Fury (Orc)
- -- blood_fury,if=debuff.colossus_smash.up
- if race == "Orc" and cast.able.bloodFury() and debuff.colossusSmash.exists(units.dyn5) then
+ -- blood_fury,if=buff.cooldown_reduction.down&(buff.bloodbath.up|(!talent.bloodbath.enabled&debuff.colossus_smash.up))|buff.cooldown_reduction.up&buff.recklessness.up
+ if race == "Orc" and cast.able.bloodFury() and (--[[not buff.cooldownReduction.exists()
+ and]] (buff.bloodbath.exists() or (not talent.bloodbath and debuff.colossusSmash.exists(units.dyn5)))
+ or --[[buff.cooldownReduction.exists() and]] buff.recklessness.exists())
+ then
if cast.bloodFury() then
- ui.debug("Casting Blood Fury")
+ ui.debug("Casting Blood Fury [Combat]")
return true
end
end
- -- Fireblood (Dark Iron Dwarf)
- -- fireblood,if=debuff.colossus_smash.up
- if race == "DarkIronDwarf" and cast.able.fireblood() and debuff.colossusSmash.exists(units.dyn5) then
- if cast.fireblood() then
- ui.debug("Casting Fireblood")
+ -- Berserking (Troll)
+ -- /berserking,if=buff.cooldown_reduction.down&(buff.bloodbath.up|(!talent.bloodbath.enabled&debuff.colossus_smash.up))|buff.cooldown_reduction.up&buff.recklessness.up
+ if race == "Troll" and cast.able.berserking() and (--[[not buff.cooldownReduction.exists()
+ and]] (buff.bloodbath.exists() or (not talent.bloodbath and debuff.colossusSmash.exists(units.dyn5)))
+ or --[[buff.cooldownReduction.exists() and]] buff.recklessness.exists())
+ then
+ if cast.berserking() then
+ ui.debug("Casting Berserking [Combat]")
return true
end
end
- -- Ancestral Call (Maghar Orc)
- -- ancestral_call,if=debuff.colossus_smash.up
- if race == "MagharOrc" and cast.able.ancestralCall() and debuff.colossusSmash.exists(units.dyn5) then
- if cast.ancestralCall() then
- ui.debug("Casting Ancestral Call")
+ -- Arcane Torrent (Blood Elf)
+ -- arcane_torrent,if=buff.cooldown_reduction.down&(buff.bloodbath.up|(!talent.bloodbath.enabled&debuff.colossus_smash.up))|buff.cooldown_reduction.up&buff.recklessness.up
+ if race == "BloodElf" and cast.able.arcaneTorrent() and (--[[not buff.cooldownReduction.exists()
+ and]] (buff.bloodbath.exists() or (not talent.bloodbath and debuff.colossusSmash.exists(units.dyn5)))
+ or --[[buff.cooldownReduction.exists() and]] buff.recklessness.exists())
+ then
+ if cast.arcaneTorrent() then
+ ui.debug("Casting Arcane Torrent [Combat]")
return true
end
end
end
- -- Call Action List - Colossus Aoe
- -- run_action_list,name=colossus_aoe,if=talent.demolish&active_enemies>2
- if talent.demolish and ui.useAOE(8, 3) then
- if actionList.ColossusAoe() then return true end
- end
- -- Call Action List - Colossus Execute,Target If=Min:Target.Health.Pct
- -- run_action_list,name=colossus_execute,target_if=min:target.health.pct,if=talent.demolish&variable.execute_phase
- if talent.demolish and var.executePhase then
- if actionList.ColossusExecute() then return true end
- end
- -- Call Action List - Colossus Sweep
- -- run_action_list,name=colossus_sweep,if=talent.demolish&active_enemies=2&!variable.execute_phase
- if talent.demolish and ui.useAOE(8, 2) and ui.useST(8, 3) and not var.executePhase then
- if actionList.ColossusSweep() then return true end
- end
- -- Call Action List - Colossus St
- -- run_action_list,name=colossus_st,if=talent.demolish
- if talent.demolish then
- if actionList.ColossusSt() then return true end
+ -- Bloodbath
+ -- bloodbath,if=enabled&(debuff.colossus_smash.remains>0.1|cooldown.colossus_smash.remains<5|target.time_to_die<=20)
+ if ui.alwaysCdNever("Bloodbath") and cast.able.bloodbath() and (debuff.colossusSmash.remains(units.dyn5) > 0.1
+ or cd.colossusSmash.remains() < 5 or unit.ttd(units.dyn5) <= 20)
+ then
+ if cast.bloodbath() then
+ ui.debug("Casting Bloodbath [Combat]")
+ return true
+ end
end
- -- Call Action List - Slayer Aoe
- -- run_action_list,name=slayer_aoe,if=talent.slayers_dominance&active_enemies>2
- if (talent.slayersDominance or not (talent.demolish and talent.slayersDominance)) and ui.useAOE(8, 3) then
- if actionList.SlayerAoe() then return true end
+ -- Berserker Rage
+ -- berserker_rage,if=buff.enrage.remains<0./berserker_rage,if=buff.enrage.remains<0.5
+ if ui.checked("Berserker Rage") and cast.able.berserkerRage() and buff.enrage.remains() < 0.5 then
+ if cast.berserkerRage() then
+ ui.debug("Casting Berserker Rage [Combat]")
+ return true
+ end
end
- -- Call Action List - Slayer Execute,Target If=Min:Target.Health.Pct
- -- run_action_list,name=slayer_execute,target_if=min:target.health.pct,if=talent.slayers_dominance&variable.execute_phase
- if (talent.slayersDominance or not (talent.demolish and talent.slayersDominance)) and var.executePhase then
- if actionList.SlayerExecute() then return true end
+ -- Heroic Leap
+ -- heroic_leap,if=debuff.colossus_smash.up
+ if ui.mode.mover == 1 and ui.checked("Heroic Leap") and cast.able.heroicLeap(units.dyn5) and (not var.knownCS or debuff.colossusSmash.exists(units.dyn5)) then
+ if cast.heroicLeap(units.dyn5) then
+ ui.debug("Casting Heroic Leap [Combat]")
+ return true
+ end
end
- -- Call Action List - Slayer Sweep
- -- run_action_list,name=slayer_sweep,if=talent.slayers_dominance&active_enemies=2&!variable.execute_phase
- if (talent.slayersDominance or not (talent.demolish and talent.slayersDominance)) and ui.useAOE(8, 2) and ui.useST(8, 3) and not var.executePhase then
- if actionList.SlayerSweep() then return true end
+ -- Action List - AOE
+ -- run_action_list,name=aoe,if=active_enemies>=2
+ if ui.useAOE(8,2) then
+ if actionList.AOE() then return true end
end
- -- Call Action List - Slayer St
- -- run_action_list,name=slayer_st,if=talent.slayers_dominance
- if talent.slayersDominance or not (talent.demolish and talent.slayersDominance) then
- if actionList.SlayerSt() then return true end
+ -- Action List - Single
+ -- run_action_list,name=single_target,if=active_enemies<2
+ if ui.useST(8,2) then
+ if actionList.Single() then return true end
end
end -- End Combat Rotation
end -- End Action List - Combat
@@ -1944,12 +878,36 @@ local function runRotation()
module = br.player.module
race = br.player.race
rage = br.player.power.rage
+ spell = br.player.spell
talent = br.player.talent
ui = br.player.ui
unit = br.player.unit
units = br.player.units
+ use = br.player.use
var = br.player.variables
+ var.knownCS = spell.colossusSmash.known()
+
+ -- Ensure blacklist and error-frame for Disarm exist (persist across runs)
+ if var.disarmBlacklist == nil then var.disarmBlacklist = {} end
+ var.lastDisarmTarget = var.lastDisarmTarget or nil
+ var.disarmErrorFrame = var.disarmErrorFrame or CreateFrame("Frame")
+ var.disarmErrorFrame:RegisterEvent("UI_ERROR_MESSAGE")
+ var.disarmErrorFrame:SetScript("OnEvent", function(self, event, errorType, message)
+ if message and var.lastDisarmTarget and var.disarmBlacklist then
+ local err = tostring(message):lower()
+ local disarmError = err:find("disarm") or err:find("invalid target")
+ if disarmError then
+ local npcID = br.functions.unit:GetObjectID(var.lastDisarmTarget)
+ if npcID and not var.disarmBlacklist[npcID] then
+ var.disarmBlacklist[npcID] = true
+ print("|cff8000FFBadRotations|r: Blacklisted " .. (UnitName(var.lastDisarmTarget) or "Unknown") .. " (ID: " .. npcID .. ") - Cannot Disarm")
+ end
+ end
+ var.lastDisarmTarget = nil
+ end
+ end)
+
units.get(5)
units.get(8)
units.get(8, true)
@@ -1958,6 +916,7 @@ local function runRotation()
enemies.get(8)
enemies.get(8, "target")
enemies.get(8, "player", false, true) -- makes enemies.yards8f
+ enemies.get(15)
enemies.get(20)
------------------------
@@ -1986,6 +945,8 @@ local function runRotation()
end
end
+ -- ui.chatOverlay("AOE: " .. tostring(ui.useAOE(8,2)) .. " | ST: " .. tostring(ui.useST(8,2)) .. " | Count: " .. #enemies.yards8)
+
-----------------
--- Rotations ---
-----------------
@@ -1995,7 +956,7 @@ local function runRotation()
-- Profile Stop | Pause
if not unit.inCombat() and not unit.exists("target") and var.profileStop then
var.profileStop = false
- elseif (unit.inCombat() and var.profileStop == true) or ui.pause() or unit.mounted() or unit.flying() or ui.mode.rotation == 4 then
+ elseif (unit.inCombat() and var.profileStop == true) or ui.pause() or unit.mounted() or unit.flying() then
return true
else
-----------------------
@@ -2006,6 +967,7 @@ local function runRotation()
--- Defensive Rotation ---
--------------------------
if actionList.Defensive() then return true end
+ if ui.mode.rotation == 4 then return true end
---------------------------
--- Pre-Combat Rotation ---
---------------------------
@@ -2017,8 +979,9 @@ local function runRotation()
end -- Pause
end -- runRotation
local id = 71
-if br.rotations[id] == nil then br.rotations[id] = {} end
-br._G.tinsert(br.rotations[id], {
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Warrior/Fury/BrewFuryLeveling.lua b/Rotations/Warrior/Fury/BrewFuryLeveling.lua
index 80ccb4e4ca..c74496a6b7 100644
--- a/Rotations/Warrior/Fury/BrewFuryLeveling.lua
+++ b/Rotations/Warrior/Fury/BrewFuryLeveling.lua
@@ -108,11 +108,11 @@ local function createOptions()
br.ui:checkSectionState(section)
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
- br.ui:createDropdown(section, "Rotation Mode", br.dropOptions.Toggle, 4)
- br.ui:createDropdown(section, "Cooldown Mode", br.dropOptions.Toggle, 3)
- br.ui:createDropdown(section, "Defensive Mode", br.dropOptions.Toggle, 6)
- br.ui:createDropdown(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
+ br.ui:createDropdown(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 3)
+ br.ui:createDropdown(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = {{
@@ -282,9 +282,9 @@ local function runRotation() -- This is the main profile loop, any below this po
if var.lastCast == nil then var.lastCast=ui.time() end
- ui.mode.Debug = br.data.settings[br.selectedSpec].toggles["Debugs"]
- ui.mode.Charge = br.data.settings[br.selectedSpec].toggles["Charge"]
- ui.mode.HeroicLeap = br.data.settings[br.selectedSpec].toggles["HeroicLeap"]
+ ui.mode.Debug = br.data.settings[br.loader.selectedSpec].toggles["Debugs"]
+ ui.mode.Charge = br.data.settings[br.loader.selectedSpec].toggles["Charge"]
+ ui.mode.HeroicLeap = br.data.settings[br.loader.selectedSpec].toggles["HeroicLeap"]
-- Explanations on the Units and Enemies functions can be found in System/API/Units.lua and System/API/Enemies.lua
-------------
@@ -429,9 +429,10 @@ local function runRotation() -- This is the main profile loop, any below this po
end -- Pause
end -- End runRotation
local id = 72 -- Change to the spec id profile is for. Spec ID can be found at: https://wowpedia.fandom.com/wiki/SpecializationID
+local expansion = br.isMOP
-- DO NOT EDIT ANYTHING BELOW THIS LINE, WILL BREAK PROFILE --
-if br.rotations[id] == nil then br.rotations[id] = {} end
-tinsert(br.rotations[id],{
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+tinsert(br.loader.rotations[id],{
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Warrior/Fury/FuryCuteOne.lua b/Rotations/Warrior/Fury/FuryCuteOne.lua
index 5293ef1741..0355b65d8a 100644
--- a/Rotations/Warrior/Fury/FuryCuteOne.lua
+++ b/Rotations/Warrior/Fury/FuryCuteOne.lua
@@ -146,17 +146,17 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdown(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdown(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
--Cooldown Key Toggle
- br.ui:createDropdown(section, "Cooldown Mode", br.dropOptions.Toggle, 3)
+ br.ui:createDropdown(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 3)
--Defensive Key Toggle
- br.ui:createDropdown(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupts Key Toggle
- br.ui:createDropdown(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
-- Mover Toggle
- br.ui:createDropdown(section, "Mover Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Mover Mode", br.ui.dropOptions.Toggle, 6)
-- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -206,8 +206,8 @@ actionList.Extras = function()
end -- End Dummy Test
-- Battle Shout
if ui.checked("Battle Shout") and cast.able.battleShout() then
- for i = 1, #br.friend do
- local thisUnit = br.friend[i].unit
+ for i = 1, #br.engines.healingEngine.friend do
+ local thisUnit = br.engines.healingEngine.friend[i].unit
if not unit.deadOrGhost(thisUnit) and unit.distance(thisUnit) < 100 and buff.battleShout.remain(thisUnit) < 600 then
if cast.battleShout() then
ui.debug("Casting Battle Shout [Extras]")
@@ -1330,9 +1330,9 @@ actionList.PreCombat = function()
end
-- Potion
-- potion
- -- if ui.checked("Potion") and br.canUseItem(142117) and unit.instance("raid") then
+ -- if ui.checked("Potion") and br.functions.item:canUseItem(142117) and unit.instance("raid") then
-- if feralSpiritRemain > 5 and not buff.prolongedPower.exists() then
- -- br.useItem(142117)
+ -- br.functions.item:useItem(142117)
-- end
-- end
end -- End Pre-Pull
@@ -1555,8 +1555,9 @@ local function runRotation()
end -- Pause
end -- End runRotation
local id = 72
-if br.rotations[id] == nil then br.rotations[id] = {} end
-br._G.tinsert(br.rotations[id], {
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/Warrior/Initial/InitialWarrior.lua b/Rotations/Warrior/Initial/InitialWarrior.lua
index 7ca17d1082..d31a32462f 100644
--- a/Rotations/Warrior/Initial/InitialWarrior.lua
+++ b/Rotations/Warrior/Initial/InitialWarrior.lua
@@ -37,15 +37,15 @@ local function createOptions()
-----------------------
section = br.ui:createSection(br.ui.window.profile, "General")
-- Basic Trinket Module
- br.player.module.BasicTrinkets(nil, section)
+ -- br.player.module.BasicTrinkets(nil, section)
-- Battle Shout
- br.ui:createCheckbox(section, "Battle Shout")
+ -- br.ui:createCheckbox(section, "Battle Shout")
-- Charge
br.ui:createCheckbox(section, "Charge")
-- Hamstring
- br.ui:createCheckbox(section, "Hamstring")
+ -- br.ui:createCheckbox(section, "Hamstring")
-- Heroic Throw
- br.ui:createCheckbox(section, "Heroic Throw")
+ -- br.ui:createCheckbox(section, "Heroic Throw")
-- Victory Rush
br.ui:createCheckbox(section, "Victory Rush")
br.ui:checkSectionState(section)
@@ -53,17 +53,17 @@ local function createOptions()
--- DEFENSIVE OPTIONS ---
-------------------------
section = br.ui:createSection(br.ui.window.profile, "Defensive")
+ -- Basic Healing Module
+ -- br.player.module.BasicHealing(section)
-- Shield Block
- br.ui:createSpinner(section, "Shield Block", 30, 0, 100, 5, "|cffFFFFFFPet Health Percent to Cast At")
+ -- br.ui:createSpinner(section, "Shield Block", 30, 0, 100, 5, "|cffFFFFFFPet Health Percent to Cast At")
br.ui:checkSectionState(section)
-------------------------
--- INTERRUPT OPTIONS ---
-------------------------
section = br.ui:createSection(br.ui.window.profile, "Interrupt")
- -- Basic Healing Module
- br.player.module.BasicHealing(section)
-- Pummel
- br.ui:createCheckbox(section, "Pummel", "|cffFFFFFFUse Pummel")
+ -- br.ui:createCheckbox(section, "Pummel", "|cffFFFFFFUse Pummel")
-- Interrupt Percentage
br.ui:createSpinnerWithout(section, "Interrupt At", 0, 0, 95, 5,
"|cffFFFFFFCast Percent to Cast At (0 is random)")
@@ -73,13 +73,11 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdownWithout(section, "Rotation Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Rotation Mode", br.ui.dropOptions.Toggle, 6)
-- Defensive Key Toggle
- br.ui:createDropdownWithout(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupts Key Toggle
- br.ui:createDropdownWithout(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
- -- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdownWithout(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = { {
@@ -102,9 +100,9 @@ local module
local ui
local unit
local units
+local var
-- General Locals
-local haltProfile
-local profileStop
+
-- Profile Specific Locals
local actionList = {}
@@ -113,22 +111,22 @@ local actionList = {}
--------------------
-- Action List -Extras
actionList.Extras = function()
- -- Battle Shout
- if ui.checked("Battle Shout") then
- if cast.able.battleShout() and buff.battleShout.refresh() and not unit.resting() then
- if cast.battleShout() then
- ui.debug("Casting Battle Shout")
- return true
- end
- end
- end
- -- Hamstring
- if ui.checked("Hamstring") and cast.able.hamstring("target") and not unit.facing("target", "player") and unit.moving("target") then
- if cast.hamstring("target") then
- ui.debug("Casting Hamstring")
- return true
- end
- end
+ -- -- Battle Shout
+ -- if ui.checked("Battle Shout") then
+ -- if cast.able.battleShout() and buff.battleShout.refresh() and not unit.resting() then
+ -- if cast.battleShout() then
+ -- ui.debug("Casting Battle Shout")
+ -- return true
+ -- end
+ -- end
+ -- end
+ -- -- Hamstring
+ -- if ui.checked("Hamstring") and cast.able.hamstring("target") and not unit.facing("target", "player") and unit.moving("target") then
+ -- if cast.hamstring("target") then
+ -- ui.debug("Casting Hamstring")
+ -- return true
+ -- end
+ -- end
end -- End Action List -Extras
-- Action List - Defensive
@@ -137,14 +135,14 @@ actionList.Defensive = function()
-- Basic Healing Module
module.BasicHealing()
--Shield Block
- if ui.checked("Shield Block") and cast.able.shieldBlock() and unit.exists("target")
- and unit.distance("target") < 5 and unit.hp() < ui.value("Shield Block")
- then
- if cast.shieldBlock() then
- ui.debug("Casting Shield Block")
- return true
- end
- end
+ -- if ui.checked("Shield Block") and cast.able.shieldBlock() and unit.exists("target")
+ -- and unit.distance("target") < 5 and unit.hp() < ui.value("Shield Block")
+ -- then
+ -- if cast.shieldBlock() then
+ -- ui.debug("Casting Shield Block")
+ -- return true
+ -- end
+ -- end
end
end -- End Action List - Defensive
@@ -152,37 +150,51 @@ end -- End Action List - Defensive
actionList.Interrupt = function()
if ui.useInterrupt() then
-- Pummel
- if ui.checked("Pummel") then
- for i = 1, #enemies.yards5f do
- local thisUnit = enemies.yards5f[i]
- if cast.able.pummel(thisUnit) and unit.interruptable(thisUnit, ui.value("Interrupt At")) then
- if cast.pummel(thisUnit) then
- ui.debug("Casting Pummel on " .. unit.name(thisUnit))
- return true
- end
- end
- end
- end
+ -- if ui.checked("Pummel") then
+ -- for i = 1, #enemies.yards5f do
+ -- local thisUnit = enemies.yards5f[i]
+ -- if cast.able.pummel(thisUnit) and unit.interruptable(thisUnit, ui.value("Interrupt At")) then
+ -- if cast.pummel(thisUnit) then
+ -- ui.debug("Casting Pummel on " .. unit.name(thisUnit))
+ -- return true
+ -- end
+ -- end
+ -- end
+ -- end
end
end -- End Action List - Interrupt
-- Action List - Pre-Combat
actionList.PreCombat = function()
if not unit.inCombat() and not unit.mounted() and unit.valid("target") then
- -- Whirlwind
- if ui.useAOE(8, 2) and unit.level() >= 9 and cast.able.whirlwind() then
- if cast.whirlwind() then
- ui.debug("Casting Whirlwind")
+ -- Execute
+ if unit.hp("target") < 20 and cast.able.execute() then
+ if cast.execute("target") then
+ ui.debug("Casting Execute [Precombat]")
+ return true
+ end
+ end
+ -- Victory Rush
+ if ui.checked("Victory Rush") and cast.able.victoryRush("target") and buff.victorious.exists() then
+ if cast.victoryRush("target") then
+ ui.debug("Casting Victory Rush [Precombat]")
return true
end
end
- -- Slam
- if (ui.useST(8, 2) or unit.level() < 9) and cast.able.slam("target") then
- if cast.slam("target") then
- ui.debug("Casting Slam")
+ -- Heroic Strike
+ if cast.able.heroicStrike("target") then
+ if cast.heroicStrike("target") then
+ ui.debug("Casting Heroic Strike [Precombat]")
return true
end
end
+ -- -- Attack
+ -- if cast.able.attack("target") then
+ -- if cast.attack("target") then
+ -- ui.debug("Casting Attack [Precombat]")
+ -- return true
+ -- end
+ -- end
-- Start Attack
if cast.able.autoAttack("target") then
if cast.autoAttack("target") then
@@ -195,77 +207,61 @@ end -- End Action List - PreCombat
-- Action List - Combat
actionList.Combat = function()
- if unit.valid("target") and cd.global.remain() == 0 then
- if unit.exists(units.dyn40) and unit.distance(units.dyn40) < 40 then
- -- Charge
- if mode.mover == 1 and ui.checked("Charge") and cast.able.charge("target")
- and unit.distance("target") >= 8 and unit.distance("target") <= 25
- then
- if cast.charge("target") then
- ui.debug("Casting Charge")
+ if (unit.inCombat() or (not unit.inCombat() and unit.valid(units.dyn5))) and not var.profileStop
+ and unit.exists(units.dyn5) and cd.global.remain() == 0
+ then
+ -- Charge
+ if mode.mover == 1 and ui.checked("Charge") and cast.able.charge("target")
+ and unit.distance("target") >= 8 and unit.distance("target") <= 25
+ then
+ if cast.charge("target") then
+ ui.debug("Casting Charge")
+ return true
+ end
+ end
+ -- Interrupts
+ if actionList.Interrupt() then return true end
+ if unit.inCombat() then
+ -- Basic Trinkets Module
+ module.BasicTrinkets()
+ -- Execute
+ if unit.hp("target") < 20 and cast.able.execute("target") then
+ if cast.execute() then
+ ui.debug("Casting Execute")
return true
end
end
- -- Heroic Throw
- if ui.checked("Heroic Throw") and cast.able.heroicThrow("target")
- and unit.distance("target") >= 8 and unit.distance("target") <= 25
- then
- if cast.heroicThrow("target") then
- ui.debug("Casting Heroic Throw")
+ -- Victory Rush
+ if ui.checked("Victory Rush") and cast.able.victoryRush("target") and buff.victorious.exists() then
+ if cast.victoryRush("target") then
+ ui.debug("Casting Victory Rush")
return true
end
end
- -- Interrupts
- if actionList.Interrupt() then return true end
- -- Execute
- if unit.inCombat() then
- if unit.hp("target") < 20 and cast.able.execute() then
- if cast.execute() then
- ui.debug("Casting Execute")
- return true
- end
- end
- -- Victory Rush
- if ui.checked("Victory Rush") and cast.able.victoryRush("target") and buff.victorious.exists() then
- if cast.victoryRush("target") then
- ui.debug("Casting Victory Rush")
- return true
- end
- end
- -- Start Attack
- if not cast.auto.autoAttack(units.dyn5) and unit.exists(units.dyn5) and unit.distance(units.dyn5) < 5 then
- if cast.autoAttack(units.dyn5) then
- ui.debug("Casting Auto Attack")
- return true
- end
- end
- -- Basic Trinkets Module
- module.BasicTrinkets()
- -- Shield Slam
- if cast.able.shieldSlam() then
- if cast.shieldSlam() then
- ui.debug("Casting Shield Slam")
- return true
- end
- end
- -- Whirlwind
- if ui.useAOE(8, 2) and unit.level() >= 9 and cast.able.whirlwind() then
- if cast.whirlwind() then
- ui.debug("Casting Whirlwind")
- return true
- end
+ -- Heroic Strike
+ if cast.able.heroicStrike() then
+ if cast.heroicStrike() then
+ ui.debug("Casting Heroic Strike")
+ return true
end
- -- Slam
- if (ui.useST(8, 2) or unit.level() < 9) and cast.able.slam() then
- if cast.slam() then
- ui.debug("Casting Slam")
- return true
- end
+ end
+ -- Attack
+ -- if cast.able.attack() then
+ -- if cast.attack() then
+ -- ui.debug("Casting Attack")
+ -- return true
+ -- end
+ -- end
+ -- Start Attack
+ if not cast.auto.autoAttack(units.dyn5) and unit.exists(units.dyn5) and unit.distance(units.dyn5) < 5 then
+ if cast.autoAttack(units.dyn5) then
+ ui.debug("Casting Auto Attack")
+ return true
end
end
end -- End In Combat Rotation
end
-end -- End Action List - Combat
+end -- End Action List - Combat
----------------
--- ROTATION ---
@@ -275,18 +271,23 @@ local function runRotation()
--- Define Locals ---
---------------------
-- BR API Locals
- buff = br.player.buff
- cast = br.player.cast
- cd = br.player.cd
- enemies = br.player.enemies
- mode = br.player.ui.mode
- module = br.player.module
- ui = br.player.ui
- unit = br.player.unit
- units = br.player.units
- -- General Locals
- profileStop = profileStop or false
- haltProfile = (unit.inCombat() and profileStop) or unit.mounted() or br.pause() or mode.rotation == 2
+ if not br.player.initialized then
+ buff = br.player.buff
+ cast = br.player.cast
+ cd = br.player.cd
+ enemies = br.player.enemies
+ mode = br.player.ui.mode
+ module = br.player.module
+ ui = br.player.ui
+ unit = br.player.unit
+ units = br.player.units
+ var = br.player.variables
+ -- General Locals
+ var.profileStop = false
+
+ br.player.initialized = true
+ end
+ -- haltProfile = (unit.inCombat() and profileStop) or unit.mounted() or br.functions.misc:pause() or mode.rotation == 2
-- Units
units.get(5) -- Makes a variable called, units.dyn5
units.get(40) -- Makes a variable called, units.dyn40
@@ -294,17 +295,20 @@ local function runRotation()
-- Enemies
enemies.get(5, "player", false, true) -- Makes a variable called, enemies.yards5f
+ if not unit.inCombat() then
+ if var.profileStop then var.profileStop = false end
+ end
+
-- Pause Timer
if br.pauseTime == nil then br.pauseTime = ui.time() end
---------------------
--- Begin Profile ---
---------------------
- -- Profile Stop | Pause
- if not unit.inCombat() and not unit.exists("target") and profileStop then
- profileStop = false
- elseif haltProfile then
- br.pauseTime = ui.time()
+ -- * Profile Stop | Pause
+ if not unit.inCombat() and not unit.exists("target") and var.profileStop then
+ var.profileStop = false
+ elseif (unit.inCombat() and var.profileStop) or ui.pause() then
return true
else
---------------------------------
@@ -318,6 +322,7 @@ local function runRotation()
--- Defensive ---
-----------------
if actionList.Defensive() then return true end
+ if ui.mode.rotation == 2 then return true end
------------------
--- Pre-Combat ---
------------------
@@ -329,8 +334,9 @@ local function runRotation()
end -- Pause
end -- End runRotation
local id = 1446 -- Change to the spec id profile is for.
-if br.rotations[id] == nil then br.rotations[id] = {} end
-br._G.tinsert(br.rotations[id], {
+local expansion = br.isMOP
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+br._G.tinsert(br.loader.rotations[id], {
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/Rotations/sample rotation.lua b/Rotations/sample rotation.lua
index 2546641d12..662022e3df 100644
--- a/Rotations/sample rotation.lua
+++ b/Rotations/sample rotation.lua
@@ -71,15 +71,15 @@ local function createOptions()
----------------------
section = br.ui:createSection(br.ui.window.profile, "Toggle Keys")
-- Single/Multi Toggle
- br.ui:createDropdown(section, "Rotation Mode", br.dropOptions.Toggle, 4)
+ br.ui:createDropdown(section, "Rotation Mode", br.ui.dropOptions.Toggle, 4)
--Cooldown Key Toggle
- br.ui:createDropdown(section, "Cooldown Mode", br.dropOptions.Toggle, 3)
+ br.ui:createDropdown(section, "Cooldown Mode", br.ui.dropOptions.Toggle, 3)
--Defensive Key Toggle
- br.ui:createDropdown(section, "Defensive Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Defensive Mode", br.ui.dropOptions.Toggle, 6)
-- Interrupts Key Toggle
- br.ui:createDropdown(section, "Interrupt Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Interrupt Mode", br.ui.dropOptions.Toggle, 6)
-- Pause Toggle
- br.ui:createDropdown(section, "Pause Mode", br.dropOptions.Toggle, 6)
+ br.ui:createDropdown(section, "Pause Mode", br.ui.dropOptions.Toggle, 6)
br.ui:checkSectionState(section)
end
optionTable = {{
@@ -238,8 +238,8 @@ local function runRotation() -- This is the main profile loop, any below this po
end -- End runRotation
local id = 000 -- Change to the spec id profile is for. Spec ID can be found at: https://wowpedia.fandom.com/wiki/SpecializationID
-- DO NOT EDIT ANYTHING BELOW THIS LINE, WILL BREAK PROFILE --
-if br.rotations[id] == nil then br.rotations[id] = {} end
-tinsert(br.rotations[id],{
+if br.loader.rotations[id] == nil then br.loader.rotations[id] = {} end
+tinsert(br.loader.rotations[id],{
name = rotationName,
toggles = createToggles,
options = createOptions,
diff --git a/System/API/Buff.lua b/System/API/Buff.lua
index a7c3d0137a..f9d75b92ad 100644
--- a/System/API/Buff.lua
+++ b/System/API/Buff.lua
@@ -8,6 +8,8 @@ if br.api == nil then br.api = {} end
br.api.buffs = function(buff, k, v)
local buff = buff[k]
+ if type(v) == "table" then v = br.functions.spell:getHighestKnownRank(v) end
+
--- Cancel a buff.
-- @function buff.spell.cancel
-- @string[opt="player"] thisUnit The unit to check.
@@ -16,11 +18,11 @@ br.api.buffs = function(buff, k, v)
if thisUnit == nil then thisUnit = 'player' end
if sourceUnit == nil then sourceUnit = 'player' end
if k == "bloodLust" then
- v = br.getLustID()
+ v = br.functions.aura:getLustID()
if v == 0 then return end
end
- if br.UnitBuffID(thisUnit, v, sourceUnit) ~= nil then
- br._G.RunMacroText("/cancelaura " .. br._G.GetSpellInfo(v))
+ if br.functions.aura:UnitBuffID(thisUnit, v, sourceUnit) ~= nil then
+ br._G.RunMacroText("/cancelaura " .. br.api.wow.GetSpellInfo(v))
end
end
@@ -29,10 +31,10 @@ br.api.buffs = function(buff, k, v)
-- @treturn number
buff.count = function()
if k == "bloodLust" then
- v = br.getLustID()
+ v = br.functions.aura:getLustID()
if v == 0 then return 0 end
end
- return tonumber(br.getBuffCount(v))
+ return tonumber(br.functions.aura:getBuffCount(v))
end
--- Get the duration of a buff.
@@ -44,10 +46,10 @@ br.api.buffs = function(buff, k, v)
if thisUnit == nil then thisUnit = 'player' end
if sourceUnit == nil then sourceUnit = 'player' end
if k == "bloodLust" then
- v = br.getLustID()
+ v = br.functions.aura:getLustID()
if v == 0 then return 0 end
end
- return br.getBuffDuration(thisUnit, v, sourceUnit)
+ return br.functions.aura:getBuffDuration(thisUnit, v, sourceUnit)
end
--- Check if a buff exists.
@@ -59,10 +61,10 @@ br.api.buffs = function(buff, k, v)
if thisUnit == nil then thisUnit = 'player' end
if sourceUnit == nil then sourceUnit = 'player' end
if k == "bloodLust" then
- v = br.getLustID()
+ v = br.functions.aura:getLustID()
if v == 0 then return false end
end
- return br.UnitBuffID(thisUnit, v, sourceUnit) ~= nil
+ return br.functions.aura:UnitBuffID(thisUnit, v, sourceUnit) ~= nil
end
--- Check if a buff does NOT exists -- added for readablity into SIMC converted rotations
@@ -72,7 +74,7 @@ br.api.buffs = function(buff, k, v)
-- @treturn boolean
buff.down = function(thisUnit, sourceUnit)
if k == "bloodLust" then
- v = br.getLustID()
+ v = br.functions.aura:getLustID()
if v == 0 then return true end
end
return not buff.exists(thisUnit, sourceUnit)
@@ -87,10 +89,10 @@ br.api.buffs = function(buff, k, v)
thisUnit = thisUnit or "player"
sourceUnit = sourceUnit or "player"
if k == "bloodLust" then
- v = br.getLustID()
+ v = br.functions.aura:getLustID()
if v == 0 then return false end
end
- return br.getBuffReact(thisUnit, v, sourceUnit)
+ return br.functions.aura:getBuffReact(thisUnit, v, sourceUnit)
end
--- Get the remaining time of a buff.
@@ -102,10 +104,10 @@ br.api.buffs = function(buff, k, v)
if thisUnit == nil then thisUnit = 'player' end
if sourceUnit == nil then sourceUnit = 'player' end
if k == "bloodLust" then
- v = br.getLustID()
+ v = br.functions.aura:getLustID()
if v == 0 then return 0 end
end
- return math.abs(br.getBuffRemain(thisUnit, v, sourceUnit))
+ return math.abs(br.functions.aura:getBuffRemain(thisUnit, v, sourceUnit))
end
--- Get the remaining time of a buff. (Duplicate of br.player.buff.spell.remain)
@@ -117,10 +119,10 @@ br.api.buffs = function(buff, k, v)
if thisUnit == nil then thisUnit = 'player' end
if sourceUnit == nil then sourceUnit = 'player' end
if k == "bloodLust" then
- v = br.getLustID()
+ v = br.functions.aura:getLustID()
if v == 0 then return 0 end
end
- return math.abs(br.getBuffRemain(thisUnit, v, sourceUnit))
+ return math.abs(br.functions.aura:getBuffRemain(thisUnit, v, sourceUnit))
end
--- Check if a buff should be refreshed.
@@ -129,10 +131,24 @@ br.api.buffs = function(buff, k, v)
-- @string[opt="player"] sourceUnit The source of the buff.
-- @treturn boolean
buff.refresh = function(thisUnit, sourceUnit)
+ if thisUnit == nil then thisUnit = 'player' end
if k == "bloodLust" then
- v = br.getLustID()
+ v = br.functions.aura:getLustID()
if v == 0 then return false end
end
+
+ -- Classic: when sourceUnit is omitted, treat refresh checks as "any source".
+ -- Also: don't attempt to refresh a buff that exists in a higher rank (same name, different spellId).
+ if ((br.isClassic or br.isBC) and sourceUnit == nil) then
+ local auraInfo = br.functions.aura:UnitBuffID(thisUnit, v)
+ if auraInfo and auraInfo.spellId and auraInfo.spellId > v then
+ return false
+ end
+ local remain = math.abs(br.functions.aura:getBuffRemain(thisUnit, v))
+ local duration = br.functions.aura:getBuffDuration(thisUnit, v)
+ return remain <= duration * 0.3
+ end
+
return buff.remain(thisUnit, sourceUnit) <= buff.duration(thisUnit, sourceUnit) * 0.3
end
@@ -145,10 +161,10 @@ br.api.buffs = function(buff, k, v)
if thisUnit == nil then thisUnit = 'player' end
if sourceUnit == nil then sourceUnit = 'player' end
if k == "bloodLust" then
- v = br.getLustID()
+ v = br.functions.aura:getLustID()
if v == 0 then return 0 end
end
- return br.getBuffStacks(thisUnit, v, sourceUnit)
+ return br.functions.aura:getBuffStacks(thisUnit, v, sourceUnit)
end
--- Get the maximum stack count of a buff.
@@ -160,9 +176,10 @@ br.api.buffs = function(buff, k, v)
if thisUnit == nil then thisUnit = 'player' end
if sourceUnit == nil then sourceUnit = 'player' end
if k == "bloodLust" then
- v = br.getLustID()
+ v = br.functions.aura:getLustID()
if v == 0 then return 0 end
end
- return br.auraMaxStacks[v] and (br.auraMaxStacks[v][thisUnit] or 0) or 0
+ if not br.readers.common.auraMaxStacks then return 0 end
+ return br.readers.common.auraMaxStacks[v] and (br.readers.common.auraMaxStacks[v][thisUnit] or 0) or 0
end
end
diff --git a/System/API/CD.lua b/System/API/CD.lua
index dfd0f1f890..6353074659 100644
--- a/System/API/CD.lua
+++ b/System/API/CD.lua
@@ -22,9 +22,15 @@ br.api.cd = function(self, spell, id)
-- @return boolean
-- @within cd.spell
cd[spell].exists = function()
+ -- Special handling for global CD in Classic
+ local spellID = type(id) == "table" and (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(id) or id[1]) or id
+ if spell == "global" and (br.isClassic or br.isBC) and br.api.wow.GetGCDSpellID then
+ spellID = br.api.wow.GetGCDSpellID()
+ end
local level = br._G.UnitLevel("player")
- local spellLevel = br._G.C_Spell.GetSpellLevelLearned(id)
- local spellCD = level >= spellLevel and br.getSpellCD(id) or 99
+ local spellLevel = br._G.C_Spell.GetSpellLevelLearned(spellID) or 1
+ if level < spellLevel then return false end
+ local spellCD = br.functions.spell:getSpellCD(spellID)
return spellCD > 0
end
@@ -33,10 +39,15 @@ br.api.cd = function(self, spell, id)
-- @return number
-- @within cd.spell
cd[spell].remain = function()
+ -- Special handling for global CD in Classic
+ local spellID = type(id) == "table" and (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(id) or id[1]) or id
+ if spell == "global" and (br.isClassic or br.isBC) and br.api.wow.GetGCDSpellID then
+ spellID = br.api.wow.GetGCDSpellID()
+ end
local level = br._G.UnitLevel("player")
- local spellLevel = br._G.C_Spell.GetSpellLevelLearned(id)
- local spellCD = level >= spellLevel and br.getSpellCD(id) or 99
- return spellCD
+ local spellLevel = br._G.C_Spell.GetSpellLevelLearned(spellID) or 1
+ if level < spellLevel then return 99 end
+ return br.functions.spell:getSpellCD(spellID)
end
--- Gets the time remaining on spell cooldown or 0 if not (alternate to cd.spell.remain() incase of typo).
@@ -44,10 +55,15 @@ br.api.cd = function(self, spell, id)
-- @return number
-- @within cd.spell
cd[spell].remains = function()
+ -- Special handling for global CD in Classic
+ local spellID = type(id) == "table" and (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(id) or id[1]) or id
+ if spell == "global" and (br.isClassic or br.isBC) and br.api.wow.GetGCDSpellID then
+ spellID = br.api.wow.GetGCDSpellID()
+ end
local level = br._G.UnitLevel("player")
- local spellLevel = br._G.C_Spell.GetSpellLevelLearned(id)
- local spellCD = level >= spellLevel and br.getSpellCD(id) or 99
- return spellCD
+ local spellLevel = br._G.C_Spell.GetSpellLevelLearned(spellID) or 1
+ if level < spellLevel then return 99 end
+ return br.functions.spell:getSpellCD(spellID)
end
--- Gets the total time of the spell cooldown
@@ -55,10 +71,16 @@ br.api.cd = function(self, spell, id)
-- @return number
-- @within cd.spell
cd[spell].duration = function()
+ -- Special handling for global CD in Classic
+ local spellID = type(id) == "table" and (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(id) or id[1]) or id
+ if spell == "global" and (br.isClassic or br.isBC) and br.api.wow.GetGCDSpellID then
+ spellID = br.api.wow.GetGCDSpellID()
+ end
local level = br._G.UnitLevel("player")
- local spellLevel = br._G.C_Spell.GetSpellLevelLearned(id)
- local spellCD = br._G.C_Spell.GetSpellCooldown(id)
- return (spellCD and level >= spellLevel) and spellCD.duration or 0
+ local spellLevel = br._G.C_Spell.GetSpellLevelLearned(spellID) or 1
+ if level < spellLevel then return 0 end
+ local spellCD = br._G.C_Spell.GetSpellCooldown(spellID)
+ return spellCD and spellCD.duration or 0
end
--- Checks if the spell is not on cooldown or is (opposite of cd.spell.exists()).
@@ -66,10 +88,15 @@ br.api.cd = function(self, spell, id)
-- @return boolean
-- @within cd.spell
cd[spell].ready = function()
+ -- Special handling for global CD in Classic
+ local spellID = type(id) == "table" and (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(id) or id[1]) or id
+ if spell == "global" and (br.isClassic or br.isBC) and br.api.wow.GetGCDSpellID then
+ spellID = br.api.wow.GetGCDSpellID()
+ end
local level = br._G.UnitLevel("player")
- local spellLevel = br._G.C_Spell.GetSpellLevelLearned(id)
- local spellCD = level >= spellLevel and br.getSpellCD(id) or 99
- return spellCD == 0
+ local spellLevel = br._G.C_Spell.GetSpellLevelLearned(spellID) or 1
+ if level < spellLevel then return false end
+ return br.functions.spell:getSpellCD(spellID) == 0
end
--- Gets the duration of the spells Global Cooldown.
@@ -77,7 +104,8 @@ br.api.cd = function(self, spell, id)
-- @return number
-- @within cd.spell
cd[spell].prevgcd = function()
- return select(2, br._G.GetSpellBaseCooldown(id))
+ local spellID = type(id) == "table" and (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(id) or id[1]) or id
+ return select(2, br._G.GetSpellBaseCooldown(spellID))
end
end
@@ -94,7 +122,7 @@ br.api.itemCD = function(self, item, id)
-- @return boolean
-- @within cd.item
cd[item].exists = function()
- return br._G.GetItemCooldown(id) > 0
+ return br._G.C_Container.GetItemCooldown(id) > 0
end
--- Gets the time remaining on item cooldown or 0 if not.
@@ -102,8 +130,8 @@ br.api.itemCD = function(self, item, id)
-- @return number
-- @within cd.item
cd[item].remain = function()
- if br._G.GetItemCooldown(id) ~= 0 then
- return (br._G.GetItemCooldown(id) + select(2, br._G.GetItemCooldown(id)) - br._G.GetTime())
+ if br._G.C_Container.GetItemCooldown(id) ~= 0 then
+ return (br._G.C_Container.GetItemCooldown(id) + select(2, br._G.C_Container.GetItemCooldown(id)) - br._G.GetTime())
end
return 0
end
diff --git a/System/API/Cast.lua b/System/API/Cast.lua
index 4be34b1f77..b449bf9ba6 100644
--- a/System/API/Cast.lua
+++ b/System/API/Cast.lua
@@ -22,6 +22,7 @@ br.api.cast = function(self, spell, id)
if cast.inFlightRemain == nil then cast.inFlightRemain = {} end
if cast.last == nil then cast.last = {} end
if cast.last.time == nil then cast.last.time = {} end
+ if cast.macro == nil then cast.macro = {} end
if cast.noControl == nil then cast.noControl = {} end
if cast.pool == nil then cast.pool = {} end
if cast.range == nil then cast.range = {} end
@@ -29,6 +30,7 @@ br.api.cast = function(self, spell, id)
if cast.safe == nil then cast.safe = {} end
if cast.time == nil then cast.time = {} end
if cast.timeSinceLast == nil then cast.timeSinceLast = {} end
+ local resolvedID = type(id) == "table" and (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(id) or id[1]) or id
--- Cast a spell based on various parameters.
-- The function name is dynamically generated based on the spell name.
@@ -43,7 +45,7 @@ br.api.cast = function(self, spell, id)
-- @tab enemies A table of enemy units that the spell should be cast on.
-- @treturn boolean
cast[spell] = function(thisUnit, castType, minUnits, effectRng, predict, predictPad, enemies)
- return br.createCastFunction(thisUnit, castType, minUnits, effectRng, id, spell, predict, predictPad, enemies)
+ return br.functions.cast:createCastFunction(thisUnit, castType, minUnits, effectRng, id, spell, predict, predictPad, enemies)
end
--- Cast a spell by its ID based on various parameters.
@@ -58,7 +60,8 @@ br.api.cast = function(self, spell, id)
-- @tab enemies A table of enemy units that the spell should be cast on.
-- @treturn boolean
cast.id = function(spellID, thisUnit, castType, minUnits, effectRng, predict, predictPad, enemies)
- return br.createCastFunction(thisUnit, castType, minUnits, effectRng, spellID, spell, predict, predictPad,
+ if type(spellID) == "table" then spellID = (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(spellID)) or spellID[1] end
+ return br.functions.cast:createCastFunction(thisUnit, castType, minUnits, effectRng, spellID, spell, predict, predictPad,
enemies)
end
@@ -74,8 +77,8 @@ br.api.cast = function(self, spell, id)
-- @tab enemies A table of enemy units that the spell should be cast on.
-- @treturn boolean
cast.name = function(spellName, thisUnit, castType, minUnits, effectRng, predict, predictPad, enemies)
- local _, _, _, _, _, _, spellID = br._G.GetSpellInfo(spellName)
- return br.createCastFunction(thisUnit, castType, minUnits, effectRng, spellID, spell, predict, predictPad,
+ local _, _, _, _, _, _, spellID = br.api.wow.GetSpellInfo(spellName)
+ return br.functions.cast:createCastFunction(thisUnit, castType, minUnits, effectRng, spellID, spell, predict, predictPad,
enemies)
end
@@ -92,7 +95,7 @@ br.api.cast = function(self, spell, id)
-- @tab enemies A table of enemy units that the spell should be cast on.
-- @treturn boolean
cast.able[spell] = function(thisUnit, castType, minUnits, effectRng, predict, predictPad, enemies)
- return br.createCastFunction(thisUnit, castType, minUnits, effectRng, id, spell, predict, predictPad, enemies,
+ return br.functions.cast:createCastFunction(thisUnit, castType, minUnits, effectRng, id, spell, predict, predictPad, enemies,
true)
end
@@ -108,7 +111,7 @@ br.api.cast = function(self, spell, id)
-- @tab enemies A table of enemy units that the spell should be cast on.
-- @treturn boolean
cast.able.id = function(spellID, thisUnit, castType, minUnits, effectRng, predict, predictPad, enemies)
- return br.createCastFunction(thisUnit, castType, minUnits, effectRng, spellID, spell, predict, predictPad,
+ return br.functions.cast:createCastFunction(thisUnit, castType, minUnits, effectRng, spellID, spell, predict, predictPad,
enemies, true)
end
@@ -124,8 +127,8 @@ br.api.cast = function(self, spell, id)
-- @tab enemies A table of enemy units that the spell should be cast on.
-- @treturn boolean
cast.able.name = function(spellName, thisUnit, castType, minUnits, effectRng, predict, predictPad, enemies)
- local _, _, _, _, _, _, spellID = br._G.GetSpellInfo(spellName)
- return br.createCastFunction(thisUnit, castType, minUnits, effectRng, spellID, spell, predict, predictPad,
+ local _, _, _, _, _, _, spellID = br.api.wow.GetSpellInfo(spellName)
+ return br.functions.cast:createCastFunction(thisUnit, castType, minUnits, effectRng, spellID, spell, predict, predictPad,
enemies, true)
end
@@ -135,14 +138,14 @@ br.api.cast = function(self, spell, id)
-- @treturn boolean
cast.active[spell] = function(thisUnit)
if thisUnit == nil then thisUnit = "player" end
- return br.isCastingSpell(id, thisUnit)
+ return br.functions.cast:isCastingSpell(resolvedID, thisUnit)
end
--- Checks if the spell is set to auto-repeat or if it's the current spell being cast.
-- @function cast.auto.spell
-- @treturn boolean
cast.auto[spell] = function()
- return br._G.C_Spell.IsAutoRepeatSpell(br._G.GetSpellInfo(id)) or br._G.C_Spell.IsCurrentSpell(id)
+ return br._G.C_Spell.IsAutoRepeatSpell(br.api.wow.GetSpellInfo(resolvedID)) or br._G.C_Spell.IsCurrentSpell(resolvedID)
end
--- Cancels the current spell being cast if it matches the specified spell.
@@ -164,9 +167,9 @@ br.api.cast = function(self, spell, id)
cast.cost[spell] = function(altPower)
if altPower == nil then altPower = false end
if altPower then
- return select(2, br.getSpellCost(id))
+ return select(2, br.functions.power:getSpellCost(resolvedID))
else
- return select(1, br.getSpellCost(id))
+ return select(1, br.functions.power:getSpellCost(resolvedID))
end
end
@@ -176,7 +179,7 @@ br.api.cast = function(self, spell, id)
-- @treturn boolean
cast.current[spell] = function(thisUnit)
if thisUnit == nil then thisUnit = "player" end
- return br.isCastingSpell(id, thisUnit)
+ return br.functions.cast:isCastingSpell(resolvedID, thisUnit)
end
--- Gets the spell id of the current (or previously) cast spell by the API.
@@ -192,14 +195,14 @@ br.api.cast = function(self, spell, id)
-- @treturn boolean
cast.dispel[spell] = function(thisUnit)
if thisUnit == nil then thisUnit = "target" end
- return br.canDispel(thisUnit, id) or false
+ return br.functions.aura:canDispel(thisUnit, resolvedID) or false
end
--- Gets current empowered rank of the spell or 0 if not empowered.
-- @function cast.empowered.spell
-- @treturn number
cast.empowered[spell] = function()
- return br.getEmpoweredRank(id)
+ return br.functions.spell:getEmpoweredRank(resolvedID)
end
--- Casts the form corresponding to the provided formIndex number.
@@ -217,7 +220,7 @@ br.api.cast = function(self, spell, id)
-- @treturn boolean
cast.inFlight[spell] = function(thisUnit)
if thisUnit == nil then thisUnit = "target" end
- return br.InFlight.Check(id, thisUnit)
+ return br.functions.InFlight:Check(resolvedID, thisUnit)
end
--- Gets time remaining on a spell in flight to the target.
@@ -225,7 +228,7 @@ br.api.cast = function(self, spell, id)
-- @string thisUnit The target unit to check for the spell in flight.
-- @treturn number
cast.inFlightRemain[spell] = function(thisUnit)
- return br.InFlight.Remain(id, thisUnit)
+ return br.functions.InFlight:Remain(resolvedID, thisUnit)
end
--- Checks if the spell was the last one cast or not.
@@ -233,25 +236,33 @@ br.api.cast = function(self, spell, id)
-- @number index Number of last cast spell up to 10 previous spells, default value is 1 if not provided.
-- @treturn boolean
cast.last[spell] = function(index)
- local tracker = br.lastCastTable.tracker
+ local tracker = br.functions.lastCast.lastCastTable.tracker
index = index or 1
- return tracker[index] and tracker[index] == id
+ return tracker[index] and tracker[index] == resolvedID
end
--- Gets the GetTime() value the last cast of this spell occurred.
-- @function cast.last.time.spell
-- @treturn number
cast.last.time[spell] = function()
- if br.lastCastTable.castTime[id] == nil then br.lastCastTable.castTime[id] = br._G.GetTime() end
- return br.lastCastTable.castTime[id]
+ if br.functions.lastCast.lastCastTable.castTime[resolvedID] == nil then br.functions.lastCast.lastCastTable.castTime[resolvedID] = br._G.GetTime() end
+ return br.functions.lastCast.lastCastTable.castTime[resolvedID]
+ end
+
+ --- Runs the macro text passed to it.
+ -- @function cast.macro
+ -- @string macroText The macro text to be run.
+ -- @treturn boolean
+ cast.macro = function(macroText)
+ br._G.RunMacroText(macroText)
+ return true
end
--- Checks if the spell can free you of a "no control" effect.
-- @function cast.noControl.spell
-- @treturn boolean
cast.noControl[spell] = function()
- local hasNoControl = br.hasNoControl
- return hasNoControl(id)
+ return br.functions.combat:hasNoControl(resolvedID)
end
--- Casts special opener condition spell.
@@ -296,14 +307,14 @@ br.api.cast = function(self, spell, id)
-- @function cast.range.spell
-- @treturn number
cast.range[spell] = function()
- return br.getSpellRange(id)
+ return br.functions.spell:getSpellRange(resolvedID)
end
--- Gets the amount of power spell will generate when cast.
-- @function cast.regen.spell
-- @treturn number
cast.regen[spell] = function()
- return br.getCastingRegen(id)
+ return br.functions.spell:getCastingRegen(resolvedID)
end
--- Checks if safe to cast specified aoe spell on unit given the aoe dimensions.
@@ -314,15 +325,15 @@ br.api.cast = function(self, spell, id)
-- @number effectRng Specify the AoE's effect range to determine units hit by it.
-- @treturn boolean
cast.safe[spell] = function(thisUnit, aoeType, minUnits, effectRng)
- return br.isSafeToAoE(id, thisUnit, effectRng, minUnits, aoeType)
+ return br.functions.range:isSafeToAoE(resolvedID, thisUnit, effectRng, minUnits, aoeType)
end
--- Gets the cast time of player's spell. If the spell has no cast time, it returns the global cooldown.
-- @function cast.time.spell
-- @treturn number
cast.time[spell] = function()
- local castTime = br.getCastTime(id)
- return castTime > 0 and castTime or br.getGlobalCD(true)
+ local castTime = br.functions.cast:getCastTime(resolvedID)
+ return castTime > 0 and castTime or br.functions.spell:getGlobalCD(true)
end
--- Gets the cast time remaining on player's cast or supplied target.
@@ -331,15 +342,15 @@ br.api.cast = function(self, spell, id)
-- @treturn number
cast.timeRemain = function(thisUnit)
if thisUnit == nil then thisUnit = "player" end
- return br.getCastTimeRemain(thisUnit)
+ return br.functions.cast:getCastTimeRemain(thisUnit)
end
--- Gets the time since the last cast of this spell occurred.
-- @function cast.timeSinceLast.spell
-- @treturn number
cast.timeSinceLast[spell] = function()
- if br.lastCastTable.castTime[id] == nil then br.lastCastTable.castTime[id] = br._G.GetTime() end
- return br._G.GetTime() - br.lastCastTable.castTime[id]
+ if br.functions.lastCast.lastCastTable.castTime[resolvedID] == nil then br.functions.lastCast.lastCastTable.castTime[resolvedID] = br._G.GetTime() end
+ return br._G.GetTime() - br.functions.lastCast.lastCastTable.castTime[resolvedID]
end
--- Checks if the provided condition is true else it waits for 0.1sec.
@@ -353,4 +364,59 @@ br.api.cast = function(self, spell, id)
return true
end
end
-end
+
+ -- ===========================
+ -- SPELL RANK SYSTEM (CLASSIC)
+ -- ===========================
+
+ if cast.rank == nil then cast.rank = {} end
+
+ --- Cast a specific rank of a spell (Classic WoW only).
+ -- Allows casting a lower rank of a spell for mana efficiency or specific mechanics.
+ -- @function cast.rank.spell
+ -- @number rankNum The rank number to cast (1-based index, e.g., 1 for Rank 1, 2 for Rank 2)
+ -- @string thisUnit The target unit for the spell. Can be standard WoW units, dynamic units, or special parameters.
+ -- @string castType Defines the type of AoE or special cast conditions.
+ -- @number minUnits Minimum number of units needed to be hit by AoE spell.
+ -- @number effectRng The AoE's effect range.
+ -- @bool predict If true, will attempt to predict enemy movements for ground location AoE spells.
+ -- @bool predictPad Pads the prediction cast time. 'predict' must be true.
+ -- @tab enemies A table of enemy units that the spell should be cast on.
+ -- @treturn boolean
+ -- @usage
+ -- -- Cast Rank 1 Healing Touch for mana efficiency
+ -- if cast.rank.healingTouch(1, "target") then return true end
+ --
+ -- -- Cast Rank 3 Rejuvenation
+ -- if cast.rank.rejuvenation(3, "target") then return true end
+ cast.rank[spell] = function(rankNum, thisUnit, castType, minUnits, effectRng, predict, predictPad, enemies)
+ if not br.isClassic and not br.isBC then
+ -- In non-Classic, just use the regular spell (no ranks)
+ return br.functions.cast:createCastFunction(thisUnit, castType, minUnits, effectRng, id, spell, predict, predictPad, enemies)
+ end
+
+ -- Get the specific rank spell ID from the spell definition (id might be a table)
+ local rankID = br.functions.spell:getSpellRank(id, rankNum)
+ if not rankID then
+ -- Rank doesn't exist, fallback to base ID
+ rankID = type(id) == "table" and id[1] or id
+ end
+
+ -- Cast the specific rank
+ return br.functions.cast:createCastFunction(thisUnit, castType, minUnits, effectRng, rankID, spell, predict, predictPad, enemies)
+ end
+
+ --- Get the highest known rank number for this spell.
+ -- @function cast.maxRank.spell
+ -- @treturn number The highest rank number the player knows (1-based), or 0 if none
+ -- @usage
+ -- local knownRank = cast.maxRank.healingTouch()
+ -- if knownRank >= 5 then
+ -- -- Player knows at least Rank 5
+ -- end
+ cast.maxRank = cast.maxRank or {}
+ cast.maxRank[spell] = function()
+ if not br.isClassic and not br.isBC then return 1 end
+ return br.functions.spell:getKnownRank(id)
+ end
+end
\ No newline at end of file
diff --git a/System/API/Charges.lua b/System/API/Charges.lua
index 39b5f0da42..0b31d52fbe 100644
--- a/System/API/Charges.lua
+++ b/System/API/Charges.lua
@@ -15,32 +15,37 @@ br.api.charges = function(self, spell, id)
-- @function charges.spell.exists
-- @return boolean
charges.exists = function()
- return br.getCharges(id) >= 1
+ local spellID = type(id) == "table" and (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(id) or id[1]) or id
+ return br.functions.spell:getCharges(spellID) >= 1
end
--- Gets the number of charges remaining on spell.
-- @function charges.spell.count
-- @return number
charges.count = function()
- return br.getCharges(id)
+ local spellID = type(id) == "table" and (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(id) or id[1]) or id
+ return br.functions.spell:getCharges(spellID)
end
charges.spellCount = function()
- return br._G.C_Spell.GetSpellCastCount(id)
+ local spellID = type(id) == "table" and (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(id) or id[1]) or id
+ return br._G.C_Spell.GetSpellCastCount(spellID)
end
--- Gets the number of charges remaining on spell as a fraction. (e.g. 1.5 charges remaining)
-- @function charges.spell.frac
-- @return number
charges.frac = function()
- return br.getChargesFrac(id)
+ local spellID = type(id) == "table" and (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(id) or id[1]) or id
+ return br.functions.spell:getChargesFrac(spellID)
end
--- Gets the maximum number of charges the spell can have.
-- @function charges.spell.max
-- @return number
charges.max = function()
- return br.getChargesFrac(id, true)
+ local spellID = type(id) == "table" and (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(id) or id[1]) or id
+ return br.functions.spell:getChargesFrac(spellID, true)
end
--- Gets the time remaining on until next charge is available.
@@ -48,10 +53,11 @@ br.api.charges = function(self, spell, id)
-- @bool[opt] chargeMax If true, returns the time remaining until all charges are available.
-- @return number
charges.recharge = function(chargeMax)
+ local spellID = type(id) == "table" and (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(id) or id[1]) or id
if chargeMax then
- return br.getRecharge(id, true)
+ return br.functions.spell:getRecharge(spellID, true)
else
- return br.getRecharge(id)
+ return br.functions.spell:getRecharge(spellID)
end
end
@@ -59,7 +65,8 @@ br.api.charges = function(self, spell, id)
-- @function charges.spell.timeTillFull
-- @return number
charges.timeTillFull = function()
- return br.getFullRechargeTime(id)
+ local spellID = type(id) == "table" and (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(id) or id[1]) or id
+ return br.functions.spell:getFullRechargeTime(spellID)
end
end
@@ -71,13 +78,13 @@ br.api.itemCharges = function(self, item, id)
-- @function charges.item.exists
-- @return boolean
charges.exists = function()
- return br.itemCharges(id) > 0
+ return br.functions.item.itemCharges(id) > 0
end
--- Gets the number of charges remaining on item.
-- @function charges.item.count
-- @return number
charges.count = function()
- return br.itemCharges(id)
+ return br.functions.item.itemCharges(id)
end
end
diff --git a/System/API/Conduit.lua b/System/API/Conduit.lua
index ac3dde8721..26134263d3 100644
--- a/System/API/Conduit.lua
+++ b/System/API/Conduit.lua
@@ -16,7 +16,7 @@ br.api.conduit = function(conduit,spell,id)
if collectionData.conduitID > 0 then
local spellID = br._G.C_Soulbinds.GetConduitSpellID(collectionData.conduitID, collectionData.conduitRank)
if spellID == id then
- local spellName, _, spellIcon = br._G.GetSpellInfo(spellID)
+ local spellName, _, spellIcon = br.api.wow.GetSpellInfo(spellID)
conduit[spell] = {
state = node.state,
icon = spellIcon,
@@ -32,7 +32,7 @@ br.api.conduit = function(conduit,spell,id)
end
end
if conduit[spell].name == nil then
- local spellName, _, spellIcon, _, _, _, spellID = br._G.GetSpellInfo(id)
+ local spellName, _, spellIcon, _, _, _, spellID = br.api.wow.GetSpellInfo(id)
--- Gets information about a specific conduit.
-- @table conduit.spell
diff --git a/System/API/Debuff.lua b/System/API/Debuff.lua
index 4c30b5df21..5a4c6ed494 100644
--- a/System/API/Debuff.lua
+++ b/System/API/Debuff.lua
@@ -12,59 +12,70 @@ if br.api == nil then br.api = {} end
-- Local function needed to facilitate debuff.calc
local function getSnapshotValue(dot)
-- Feral Bleeds
- if br._G.GetSpecializationInfo(br._G.GetSpecialization()) == 103 then
+ if br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization()) == 103 then
local multiplier = 1.00
- local Bloodtalons = 1.30
- -- local SavageRoar = 1.40
+ local DreamOfCenarius = 1
+ local Bloodtalons = 1
+ local SavageRoar = 1
local TigersFury = 1.15
local RakeMultiplier = 1
- -- Tigers Fury
+ -- * Tigers Fury
if br.player.buff.tigersFury.exists() then multiplier = multiplier * TigersFury end
- -- moonfire feral
- if dot == br.player.spells.debuffs.moonfireFeral then
- -- return moonfire
- return multiplier
+ -- * Dream of Cenarius / Bloodtalons
+ if br.isMOP then
+ DreamOfCenarius = 1.30
+ if br.player.buff.dreamOfCenarius.exists() then multiplier = multiplier * DreamOfCenarius end
end
- -- Bloodtalons
- if br.player.buff.bloodtalons.exists() and dot == br.player.spells.debuffs.rip then multiplier = multiplier *
- Bloodtalons end
- -- Savage Roar
- -- if self.buff.savageRoar.exists() then multiplier = multiplier*SavageRoar end
- -- rip
+ if br.isRetail then
+ Bloodtalons = 1.30
+ if br.player.buff.bloodtalons.exists() then multiplier = multiplier * Bloodtalons end
+ end
+ -- * Savage Roar
+ if br.isMOP then
+ SavageRoar = 1.45
+ if br.player.buff.savageRoar.exists() then multiplier = multiplier * SavageRoar end
+ end
+
+ -- Get Attack Power for actual damage calculations
+ local UnitAttackPower = br._G["UnitAttackPower"]
+ local base, posBuff, negBuff = UnitAttackPower("player")
+ local ap = base + posBuff + negBuff
+
+ -- * Rip
if dot == br.player.spells.debuffs.rip then
- -- -- Versatility
- -- multiplier = multiplier*(1+Versatility*0.1)
- -- return rip
- return 5 * multiplier
+ -- Rip: 768% AP over 16 seconds (8 ticks, one every 2 seconds)
+ local ripTickDamage = (7.68 * ap) / 8
+ return ripTickDamage * multiplier
end
- -- rake
+ -- * Rake
if dot == br.player.spells.debuffs.rake then
- -- Incarnation/Prowl/Sudden Ambush
- if br.player.buff.berserk.exists() or br.player.buff.incarnationAvatarOfAshamane.exists() or br.player.buff.prowl.exists()
- or br.player.buff.shadowmeld.exists() or br.player.buff.suddenAmbush.exists()
+ -- Rake: 155% AP over 15 seconds (9 ticks, one every 3 seconds)
+ local rakeTickDamage = (0.155 * ap) / 9
+ -- Incarnation/Prowl/Shadowmeld
+ if br.isRetail and (br.player.buff.incarnationAvatarOfAshamane.exists() or br.player.buff.prowl.exists()
+ or br.player.buff.shadowmeld.exists()) --or br.player.buff.suddenAmbush.exists()
then
- RakeMultiplier = 1.6
+ RakeMultiplier = 2.0
end
- -- return rake
- return multiplier * RakeMultiplier
+ return rakeTickDamage * multiplier * RakeMultiplier
end
end
-- Assassination Bleeds
- if br._G.GetSpecializationInfo(br._G.GetSpecialization()) == 259 then
- local multiplier = 1
- if br.player.buff.stealth.exists() and br.player.talent.nightstalker and (dot == br.player.spells.debuffs.rupture
- or dot == br.player.spells.debuffs.garrote) then
- multiplier = 1.5
- end
- if (br.player.buff.stealth.exists() or br.player.buff.vanish.exists()
- or (br.player.buff.subterfuge.exists() and br.player.buff.subterfuge.remain() >= 0.1
- and br.player.buff.subterfuge.remain() >= br.getSpellCD(61304)))
- and dot == br.player.spells.debuffs.garrote and br.player.talent.subterfuge
- then
- multiplier = 1.8
- end
- return multiplier
- end
+ -- if br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization()) == 259 then
+ -- local multiplier = 1
+ -- if br.player.buff.stealth.exists() and br.player.talent.nightstalker and (dot == br.player.spells.debuffs.rupture
+ -- or dot == br.player.spells.debuffs.garrote) then
+ -- multiplier = 1.5
+ -- end
+ -- if (br.player.buff.stealth.exists() or br.player.buff.vanish.exists()
+ -- or (br.player.buff.subterfuge.exists() and br.player.buff.subterfuge.remain() >= 0.1
+ -- and br.player.buff.subterfuge.remain() >= br.functions.spell:getSpellCD(61304)))
+ -- and dot == br.player.spells.debuffs.garrote and br.player.talent.subterfuge
+ -- then
+ -- multiplier = 1.8
+ -- end
+ -- return multiplier
+ -- end
return 0
end
@@ -78,7 +89,8 @@ local baseTickTimes = {
}
br.api.debuffs = function(debuff, k, v)
- local spec = br._G.GetSpecializationInfo(br._G.GetSpecialization())
+ local spec = br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization())
+ if type(v) == "table" then v = br.functions.spell:getHighestKnownRank(v) end
--- Checks if a debuff exists on a unit.
-- @function debuff.spell.exists
@@ -88,7 +100,7 @@ br.api.debuffs = function(debuff, k, v)
debuff.exists = function(thisUnit, sourceUnit)
if thisUnit == nil then thisUnit = 'target' end
if sourceUnit == nil then sourceUnit = 'player' end
- return br.UnitDebuffID(thisUnit, v, sourceUnit) ~= nil
+ return br.functions.aura:UnitDebuffID(thisUnit, v, sourceUnit) ~= nil
end
--- Gets the duration of a debuff on a unit.
@@ -99,7 +111,7 @@ br.api.debuffs = function(debuff, k, v)
debuff.duration = function(thisUnit, sourceUnit)
if thisUnit == nil then thisUnit = 'target' end
if sourceUnit == nil then sourceUnit = 'player' end
- return br.getDebuffDuration(thisUnit, v, sourceUnit) or 0
+ return br.functions.aura:getDebuffDuration(thisUnit, v, sourceUnit) or 0
end
--- Gets the remaining time of a debuff on a unit.
@@ -110,7 +122,7 @@ br.api.debuffs = function(debuff, k, v)
debuff.remain = function(thisUnit, sourceUnit)
if thisUnit == nil then thisUnit = 'target' end
if sourceUnit == nil then sourceUnit = 'player' end
- return math.abs(br.getDebuffRemain(thisUnit, v, sourceUnit))
+ return math.abs(br.functions.aura:getDebuffRemain(thisUnit, v, sourceUnit))
end
--- Gets the remaining time of a debuff on a unit. (Duplicate of debuff.spell.remain)
@@ -121,7 +133,7 @@ br.api.debuffs = function(debuff, k, v)
debuff.remains = function(thisUnit, sourceUnit)
if thisUnit == nil then thisUnit = 'target' end
if sourceUnit == nil then sourceUnit = 'player' end
- return math.abs(br.getDebuffRemain(thisUnit, v, sourceUnit))
+ return math.abs(br.functions.aura:getDebuffRemain(thisUnit, v, sourceUnit))
end
--- Gets the stack count of a debuff on a unit.
@@ -132,10 +144,10 @@ br.api.debuffs = function(debuff, k, v)
debuff.stack = function(thisUnit, sourceUnit)
if thisUnit == nil then thisUnit = 'target' end
if sourceUnit == nil then sourceUnit = 'player' end
- if br.getDebuffStacks(thisUnit, v, sourceUnit) == 0 and br.UnitDebuffID(thisUnit, v, sourceUnit) ~= nil then
+ if br.functions.aura:getDebuffStacks(thisUnit, v, sourceUnit) == 0 and br.functions.aura:UnitDebuffID(thisUnit, v, sourceUnit) ~= nil then
return 1
else
- return br.getDebuffStacks(thisUnit, v, sourceUnit)
+ return br.functions.aura:getDebuffStacks(thisUnit, v, sourceUnit)
end
end
@@ -165,9 +177,9 @@ br.api.debuffs = function(debuff, k, v)
if sourceUnit == nil then sourceUnit = 'player' end
if thisUnit == 'target' then thisUnit = br._G.UnitGUID("target") end
local multiplier = 0
- local duration = br.getDebuffDuration(thisUnit, v, sourceUnit) or 0
+ local duration = br.functions.aura:getDebuffDuration(thisUnit, v, sourceUnit) or 0
if duration > 0 then
- multiplier = math.abs(br.getDebuffRemain(thisUnit, v, sourceUnit)) / duration
+ multiplier = math.abs(br.functions.aura:getDebuffRemain(thisUnit, v, sourceUnit)) / duration
end
return multiplier
end
@@ -188,7 +200,7 @@ br.api.debuffs = function(debuff, k, v)
-- @function debuff.spell.count
-- @treturn number
debuff.count = function()
- return tonumber(br.getDebuffCount(v))
+ return tonumber(br.functions.aura:getDebuffCount(v))
end
--- Gets the count of units with a debuff remaining time less than a specified value.
@@ -196,7 +208,7 @@ br.api.debuffs = function(debuff, k, v)
-- @number remain The remaining time to check against.
-- @treturn number
debuff.remainCount = function(remain)
- return tonumber(br.getDebuffRemainCount(v, remain))
+ return tonumber(br.functions.aura:getDebuffRemainCount(v, remain))
end
--- Gets the count of units that should have their debuff refreshed.
@@ -205,12 +217,12 @@ br.api.debuffs = function(debuff, k, v)
-- @treturn number
debuff.refreshCount = function(range)
local counter = 0
- for l, _ in pairs(br.enemy) do
- local thisUnit = br.enemy[l].unit
+ for l, _ in pairs(br.engines.enemiesEngine.enemy) do
+ local thisUnit = br.engines.enemiesEngine.enemy[l].unit
if range == nil then range = 40 end
- local distance = br.getDistance(thisUnit, "player")
+ local distance = br.functions.range:getDistance(thisUnit, "player")
-- check if unit is valid
- if br.GetObjectExists(thisUnit) and distance <= range then
+ if br.functions.unit:GetObjectExists(thisUnit) and distance <= range then
-- increase counter for each occurences
if not debuff.refresh(thisUnit, "player") then
counter = counter + 1
@@ -228,7 +240,7 @@ br.api.debuffs = function(debuff, k, v)
debuff.lowest = function(range, debuffType, source)
if range == nil then range = 40 end
if debuffType == nil then debuffType = "remain" end
- return br.getDebuffMinMax(k, range, debuffType, "min", source) or "target"
+ return br.functions.aura:getDebuffMinMax(k, range, debuffType, "min", source) or "target"
end
--- Gets the unit with the lowest remaining time for a debuff within a specified range, considering only pets.
@@ -239,7 +251,7 @@ br.api.debuffs = function(debuff, k, v)
debuff.lowestPet = function(range, debuffType)
if range == nil then range = 8 end
if debuffType == nil then debuffType = "remain" end
- return br.getDebuffMinMaxButForPetsThisTime(k, range, debuffType, "min") or "target"
+ return br.functions.aura:getDebuffMinMaxButForPetsThisTime(k, range, debuffType, "min") or "target"
end
--- Gets the unit with the highest remaining time for a debuff within a specified range.
@@ -250,7 +262,7 @@ br.api.debuffs = function(debuff, k, v)
debuff.max = function(range, debuffType)
if range == nil then range = 40 end
if debuffType == nil then debuffType = "remain" end
- return br.getDebuffMinMax(k, range, debuffType, "max") or "target"
+ return br.functions.aura:getDebuffMinMax(k, range, debuffType, "max") or "target"
end
--- Checks if a debuff is exsanguinated.
@@ -273,9 +285,33 @@ br.api.debuffs = function(debuff, k, v)
-- @string[opt="target"] thisUnit The unit to check the debuff applied value on.
-- @treturn number
debuff.applied = function(thisUnit)
+ local thisUnit = br._G.GetObjectWithGUID(thisUnit) or thisUnit or "target"
return (spec == 103 or spec == 259) and debuff.bleed[thisUnit] or 0
end
+ --- Gets the number of debuff ticks remaining until it expores on the specified unit.
+ -- @function debuff.spell.ticksRemain
+ -- string[opt="target"] thisUnit The unit to check the remaining ticks on.
+ -- @treturn number
+ debuff.ticksRemain = function(thisUnit)
+ local haste = br._G.UnitSpellHaste("player")
+ local hasteMultiplier = 1 + (haste / 100)
+ local baseTickTime = baseTickTimes[v] or 3
+ local hastedTickTime = baseTickTime / hasteMultiplier
+ local auraInfo = br.functions.aura:UnitDebuffID(thisUnit, v, nil, "PLAYER")
+ if auraInfo then
+ local expirationTime = auraInfo.expirationTime
+ -- local duration = auraInfo.duration
+ local remainingDuration = expirationTime - br._G.GetTime()
+ local remainingTicks = math.floor(remainingDuration / hastedTickTime)
+
+ return remainingTicks -- The additional ticks gained by refreshing
+ else
+ -- Duration amount here shouldn't matter, since no DoT exists
+ return math.floor(99 / hastedTickTime) -- Return the max possible ticks for a fresh DoT
+ end
+ end
+
--- Gets the number of debuff ticks that will be gained by reapplying the debuff to the specified unit.
-- @function debuff.spell.ticksGainedOnRefresh
-- string[opt="target"] thisUnit The unit to check the ticks gained by reapplying the debuff on.
@@ -285,7 +321,7 @@ br.api.debuffs = function(debuff, k, v)
local hasteMultiplier = 1 + (haste / 100)
local baseTickTime = baseTickTimes[v] or 3
local hastedTickTime = baseTickTime / hasteMultiplier
- local auraInfo = br.UnitDebuffID(thisUnit, v, nil, "PLAYER")
+ local auraInfo = br.functions.aura:UnitDebuffID(thisUnit, v, nil, "PLAYER")
if auraInfo then
local expirationTime = auraInfo.expirationTime
local duration = auraInfo.duration
diff --git a/System/API/Enemies.lua b/System/API/Enemies.lua
index 0d448c995a..996bf57059 100644
--- a/System/API/Enemies.lua
+++ b/System/API/Enemies.lua
@@ -37,7 +37,7 @@ local function setVariable(self, unit, range, checkNoCombat, facing, type, table
type -- Ex: enemies.yards8 (returns all enemies around player in 8yrds), Adds Table Type (r for Rect, c for Cone, blank for Normal)
if unit ~= "player" then
-- letter tag on end based on type of unit passed, if target or enemy unit then "t" otherwise first letter of what is passed in: f - "focus", p - "pet", m - "mouseover", etc
- if br.units[unit] ~= nil then
+ if br.engines.enemiesEngine.units[unit] ~= nil then
insertTable = insertTable .. "t" -- Ex: enemies.yards8t (returns all enemies around target in 8yrds)
else
insertTable = insertTable ..
@@ -47,7 +47,7 @@ local function setVariable(self, unit, range, checkNoCombat, facing, type, table
if checkNoCombat then insertTable = insertTable .. "nc" end -- Ex: enemies.yards8tnc (returns all units around target in 8yrds)
if facing then insertTable = insertTable .. "f" end -- Ex: enemies.yards8tncf (returns all units the target is facing in 8yrds)
if self.enemies[insertTable] == nil then self.enemies[insertTable] = {} else br._G.wipe(self.enemies[insertTable]) end
- if count > 0 then br.insertTableIntoTable(self.enemies[insertTable], table) end
+ if count > 0 then br.functions.custom:insertTableIntoTable(self.enemies[insertTable], table) end
end
br.api.enemies = function(self)
@@ -66,7 +66,7 @@ br.api.enemies = function(self)
if unit == nil then unit = "player" end
if checkNoCombat == nil then checkNoCombat = false end
if facing == nil then facing = false end
- local enemyTable = br.getEnemies(unit, range, checkNoCombat, facing)
+ local enemyTable = br.engines.enemiesEngineFunctions:getEnemies(unit, range, checkNoCombat, facing)
-- Build enemies.yards variable
setVariable(self, unit, range, checkNoCombat, facing, "", enemyTable)
-- Backwards compatability for old way
@@ -81,7 +81,7 @@ br.api.enemies = function(self)
-- @bool showLines Show lines for the cone *non-functional atm*
-- @treturn table
enemies.cone.get = function(angle, range, checkNoCombat, showLines)
- local count, table = br.getEnemiesInCone(angle, range, checkNoCombat, showLines)
+ local count, table = br.engines.enemiesEngineFunctions:getEnemiesInCone(angle, range, checkNoCombat, showLines)
-- Build enemies.yards variable
setVariable(self, "player", range, checkNoCombat, false, "c", table, count)
-- Backwards compatability for old way
@@ -97,7 +97,7 @@ br.api.enemies = function(self)
-- @bool facing Check if enemies are facing unit *no longer used*
-- @treturn table
enemies.rect.get = function(width, range, showLines, checkNoCombat, facing)
- local count, table = br.getEnemiesInRect(width, range, showLines, checkNoCombat)
+ local count, table = br.engines.enemiesEngineFunctions:getEnemiesInRect(width, range, showLines, checkNoCombat)
-- Build enemies.yards variable
setVariable(self, "player", range, checkNoCombat, false, "r", table, count)
-- Backwards compatability for old way
diff --git a/System/API/Equiped.lua b/System/API/Equiped.lua
index e831e80d7e..9cac3288c1 100644
--- a/System/API/Equiped.lua
+++ b/System/API/Equiped.lua
@@ -17,9 +17,9 @@ br.api.equiped = function(self, item, id)
-- @treturn boolean
equiped[item] = function(slotID)
if slotID == nil then
- return br.hasEquiped(id)
+ return br.functions.item:hasEquiped(id)
else
- return br.hasEquiped(id, slotID)
+ return br.functions.item:hasEquiped(id, slotID)
end
end
@@ -33,9 +33,9 @@ br.api.equiped = function(self, item, id)
-- @usage equipes.item(12345, 1) -- will return true if item with ID 12345 is equipped in slot 1
equiped.item = function(itemID, slotID)
if slotID == nil then
- return br.hasEquiped(itemID)
+ return br.functions.item:hasEquiped(itemID)
else
- return br.hasEquiped(itemID, slotID)
+ return br.functions.item:hasEquiped(itemID, slotID)
end
end
@@ -55,8 +55,8 @@ br.api.equiped = function(self, item, id)
-- @treturn number
equiped.tier = function(tierLevel, numEquiped)
if type(tierLevel) == "number" then tierLevel = "T" .. tierLevel end
- if numEquiped == nil then return br.TierScan(tierLevel) end
- return br.TierScan(tierLevel) >= numEquiped
+ if numEquiped == nil then return br.functions.item:TierScan(tierLevel) end
+ return br.functions.item:TierScan(tierLevel) >= numEquiped
end
if equiped.type == nil then
diff --git a/System/API/Essence.lua b/System/API/Essence.lua
index 34c2670d0d..3d8f961382 100644
--- a/System/API/Essence.lua
+++ b/System/API/Essence.lua
@@ -30,8 +30,9 @@ br.api.essences = function(essence,k,v)
local milestoneTable = br._G.C_AzeriteEssence.GetMilestones()
local GetSpellInfo = br._G["GetSpellInfo"]
local C_AzeriteEssence = br._G["C_AzeriteEssence"]
- local essenceIdName = GetSpellInfo(GetSpellInfo(v)) or "None"
- local essenceIcon = select(3,GetSpellInfo(v)) or 0
+ local resolvedV = type(v) == "table" and (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(v) or v[1]) or v
+ local essenceIdName = GetSpellInfo(GetSpellInfo(resolvedV)) or "None"
+ local essenceIcon = select(3,GetSpellInfo(resolvedV)) or 0
if milestoneTable then
for i = 1, #milestoneTable do
local thisMilestone = milestoneTable[i]
@@ -56,5 +57,5 @@ br.api.essences = function(essence,k,v)
-- Other Data
essence.key = k
essence.id = v
- essence.spellID = select(7,essenceIdName) or v
+ essence.spellID = select(7,essenceIdName) or resolvedV
end
\ No newline at end of file
diff --git a/System/API/Glyph.lua b/System/API/Glyph.lua
new file mode 100644
index 0000000000..ab18bda1b1
--- /dev/null
+++ b/System/API/Glyph.lua
@@ -0,0 +1,70 @@
+---
+-- Glyph handling functions for BadRotations.
+-- Glyph functions are stored in br.player.glyph and can be utilized by `local glyph = br.player.glyph` in your profile.
+--
+-- NOTE: On modern retail clients the classic glyph socket API may not exist; in that case glyph checks return false.
+-- @module br.player.glyph
+local _, br = ...
+if br.api == nil then br.api = {} end
+
+-- Base glyph API (table-level helpers)
+br.api.glyph = function(self)
+ self.glyph = self.glyph or {}
+ local g = self.glyph
+
+ local glyphFn = br.functions and br.functions.glyph
+
+ --- Returns whether glyph API is available on this client.
+ -- @function glyph.available
+ -- @treturn boolean
+ g.available = function()
+ return glyphFn and glyphFn:isAvailable() or false
+ end
+
+ --- Returns how many glyph sockets we can scan.
+ -- @function glyph.socketCount
+ -- @treturn number
+ g.socketCount = function()
+ return glyphFn and glyphFn:getSocketCount() or 0
+ end
+
+ --- Returns true if the given glyph spellId is socketed.
+ -- @function glyph.has
+ -- @tparam number glyphSpellId
+ -- @treturn boolean
+ g.has = function(glyphSpellId)
+ return glyphFn and glyphFn:hasGlyph(glyphSpellId) or false
+ end
+
+ -- Alias for readability.
+ g.enabled = g.has
+end
+
+-- Per-glyph entry API
+br.api.glyphs = function(glyphTable, k, v)
+ local entry = glyphTable[k] or {}
+ glyphTable[k] = entry
+
+ local glyphFn = br.functions and br.functions.glyph
+
+ --- Returns the glyph spellId.
+ -- @function glyph.k.id
+ -- @treturn number
+ entry.id = function()
+ return v
+ end
+
+ --- Returns true if this glyph is socketed.
+ -- @function glyph.k.enabled
+ -- @treturn boolean
+ entry.enabled = function()
+ return glyphFn and glyphFn:hasGlyph(v) or false
+ end
+
+ -- Aliases commonly used in converted APLs.
+ entry.exists = entry.enabled
+ entry.up = entry.enabled
+ entry.down = function()
+ return not entry.enabled()
+ end
+end
diff --git a/System/API/Has.lua b/System/API/Has.lua
index 0056cb0577..ba45017ef0 100644
--- a/System/API/Has.lua
+++ b/System/API/Has.lua
@@ -17,7 +17,10 @@ br.api.has = function(self,item,id)
-- @function has.item
-- @treturn boolean
has[item] = function()
- return br.hasItem(id)
+ if type(id) == "table" then
+ return br.functions.item:getHighestHeldRank(id) ~= nil
+ end
+ return br.functions.item:hasItem(id)
end
if has.item == nil then
@@ -28,7 +31,7 @@ br.api.has = function(self,item,id)
-- @treturn boolean
has.item = function(itemID)
if itemID == nil then return end
- return br.hasItem(itemID)
+ return br.functions.item:hasItem(itemID)
end
end
diff --git a/System/API/Item.lua b/System/API/Item.lua
index f00826cc5c..25ef3f1902 100644
--- a/System/API/Item.lua
+++ b/System/API/Item.lua
@@ -20,7 +20,7 @@ br.api.items = function(item,k,v,subtable)
-- -- @treturn boolean
-- cd[k].exists = function(itemID)
-- if itemID == nil then itemID = v end
- -- return br._G.GetItemCooldown(itemID) > 0
+ -- return br._G.C_Container.GetItemCooldown(itemID) > 0
-- end
-- --- Gets the time remaining on item cooldown or 0 if not.
@@ -29,8 +29,8 @@ br.api.items = function(item,k,v,subtable)
-- -- @treturn number
-- cd[k].remain = function(itemID)
-- if itemID == nil then itemID = v end
- -- if br._G.GetItemCooldown(itemID) ~= 0 then
- -- return (br._G.GetItemCooldown(itemID) + select(2,br._G.GetItemCooldown(itemID)) - br._G.GetTime())
+ -- if br._G.C_Container.GetItemCooldown(itemID) ~= 0 then
+ -- return (br._G.C_Container.GetItemCooldown(itemID) + select(2,br._G.C_Container.GetItemCooldown(itemID)) - br._G.GetTime())
-- end
-- return 0
-- end
@@ -53,14 +53,14 @@ br.api.items = function(item,k,v,subtable)
-- -- @function charges.item.exists
-- -- @treturn boolean
-- charges[k].exists = function()
- -- return br.itemCharges(v) > 0
+ -- return br.functions.item:itemCharges(v) > 0
-- end
-- --- Gets the number of charges remaining on item.
-- -- @function charges.item.count
-- -- @treturn number
-- charges[k].count = function()
- -- return br.itemCharges(v)
+ -- return br.functions.item:itemCharges(v)
-- end
-- end
@@ -74,9 +74,9 @@ br.api.items = function(item,k,v,subtable)
-- -- @treturn boolean
-- equiped[k] = function(slotID)
-- if slotID == nil then
- -- return br.hasEquiped(v)
+ -- return br.functions.item:hasEquiped(v)
-- else
- -- return br.hasEquiped(v,slotID)
+ -- return br.functions.item:hasEquiped(v,slotID)
-- end
-- end
@@ -96,7 +96,7 @@ br.api.items = function(item,k,v,subtable)
-- -- @treturn number
-- equiped.tier = function(tierLevel)
-- if type(tierLevel) == "number" then tierLevel = "T"..tierLevel end
- -- return br.TierScan(tierLevel)
+ -- return br.functions.item:TierScan(tierLevel)
-- end
-- if equiped.type == nil then
@@ -121,7 +121,7 @@ br.api.items = function(item,k,v,subtable)
-- -- @function has.item
-- -- @treturn boolean
-- has[k] = function()
- -- return br.hasItem(v)
+ -- return br.functions.item:hasItem(v)
-- end
-- if has.item == nil then
@@ -132,7 +132,7 @@ br.api.items = function(item,k,v,subtable)
-- -- @treturn boolean
-- has.item = function(itemID)
-- if itemID == nil then return end
- -- return br.hasItem(itemID)
+ -- return br.functions.item:hasItem(itemID)
-- end
-- end
end
@@ -149,9 +149,9 @@ br.api.items = function(item,k,v,subtable)
-- use[k] = function(slotID,thisUnit)
-- if thisUnit == nil then thisUnit = "target" end
-- if slotID == nil then
- -- if br.canUseItem(v) then return br.useItem(v,thisUnit) else return end
+ -- if br.functions.item:canUseItem(v) then return br.functions.item:useItem(v,thisUnit) else return end
-- else
- -- if br.canUseItem(slotID) then return br.useItem(slotID,thisUnit) else return end
+ -- if br.functions.item:canUseItem(slotID) then return br.functions.item:useItem(slotID,thisUnit) else return end
-- end
-- end
@@ -165,8 +165,8 @@ br.api.items = function(item,k,v,subtable)
-- use.item = function(itemID,thisUnit)
-- if itemID == nil then return false end
-- if thisUnit == nil then thisUnit = "target" end
- -- if br.canUseItem(itemID) then
- -- return br.useItem(itemID,thisUnit)
+ -- if br.functions.item:canUseItem(itemID) then
+ -- return br.functions.item:useItem(itemID,thisUnit)
-- else
-- return
-- end
@@ -181,7 +181,7 @@ br.api.items = function(item,k,v,subtable)
-- -- @treturn boolean
-- use.slot = function(slotID,thisUnit)
-- if thisUnit == nil then thisUnit = "target" end
- -- if br.canUseItem(slotID) then return br.useItem(slotID,thisUnit) else return end
+ -- if br.functions.item:canUseItem(slotID) then return br.functions.item:useItem(slotID,thisUnit) else return end
-- end
-- end
@@ -192,7 +192,7 @@ br.api.items = function(item,k,v,subtable)
-- -- @number[opt] slotID The ID of the equipment slot to check.
-- -- @treturn boolean
-- use.able[k] = function(slotID)
- -- if slotID == nil then return br.canUseItem(v) else return br.canUseItem(slotID) end
+ -- if slotID == nil then return br.functions.item:canUseItem(v) else return br.functions.item:canUseItem(slotID) end
-- end
-- if use.able.item == nil then
@@ -203,7 +203,7 @@ br.api.items = function(item,k,v,subtable)
-- -- @treturn boolean
-- use.able.item = function(itemID)
-- if itemID == nil then return false end
- -- return br.canUseItem(itemID)
+ -- return br.functions.item:canUseItem(itemID)
-- end
-- end
@@ -213,7 +213,7 @@ br.api.items = function(item,k,v,subtable)
-- -- @number slotID The ID of the equipment slot to check.
-- -- @treturn boolean
-- use.able.slot = function(slotID)
- -- return br.canUseItem(slotID)
+ -- return br.functions.item:canUseItem(slotID)
-- end
-- end
end
diff --git a/System/API/Module.lua b/System/API/Module.lua
index 045b22ed66..81f0404fac 100644
--- a/System/API/Module.lua
+++ b/System/API/Module.lua
@@ -13,6 +13,7 @@ br.api.module = function(self)
local has = self.has
local item = self.items
local ui = self.ui
+ local enemies = self.enemies
local unit = self.unit
local use = self.use
local var = {}
@@ -24,7 +25,7 @@ br.api.module = function(self)
-- @string option Name of the option to check.
-- @string["Check"|"Value"] optionType Type of option check to perform
local function getOption(option, optionType)
- if br.data.settings[br.selectedSpec][br.selectedProfile]["Rotation Options"] == nil then
+ if br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["Rotation Options"] == nil then
if optionType == "Check" then return ui.checked(option, "Base Options") end
if optionType == "Value" then return ui.value(option, "Base Options") end
else
@@ -32,12 +33,12 @@ br.api.module = function(self)
if optionType == "Value" then return ui.value(option, "Rotation Options") end
end
-- if optionType == "Check" then
- -- return br.data.settings[br.selectedSpec][br.selectedProfile]["Rotation Options"][option] ~= nil and
+ -- return br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["Rotation Options"][option] ~= nil and
-- ui.checked(option, "Rotation Options") or
-- ui.checked(option, "Base Options")
-- end
-- if optionType == "Value" then
- -- return br.data.settings[br.selectedSpec][br.selectedProfile]["Rotation Options"][option] ~= nil and
+ -- return br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["Rotation Options"][option] ~= nil and
-- ui.value(option, "Rotation Options") or
-- ui.value(option, "Base Options")
-- end
@@ -97,7 +98,7 @@ br.api.module = function(self)
local itemName, _, _, itemLevel, itemMinLevel, itemType, itemSubType, _, _, _, _, _, _, _, _, _, _ =
C_Item.GetItemInfo(itemInfo.itemID)
if itemType == "Consumable" and itemSubType == "Potions" then
- if string.find(itemName, "Heal", 0, true) then
+ if string.find(itemName, "Heal", 1, true) then
table.insert(Consumables, #Consumables + 1, {
itemID = itemInfo.itemID,
spellId = spellID,
@@ -115,7 +116,7 @@ br.api.module = function(self)
end
table.sort(Consumables, function(v1, v2) return v1.itemLevel > v2.itemLevel end)
for i = 1, #Consumables do
- if br.canUseItem(Consumables[i].itemID) then return Consumables[i].itemID end
+ if br.functions.item:canUseItem(Consumables[i].itemID) then return Consumables[i].itemID end
end
return nil
end
@@ -130,22 +131,22 @@ br.api.module = function(self)
br.ui:createSpinner(section, "Healthstone/Potion", 60, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
-- Heirloom Neck
br.ui:createSpinner(section, "Heirloom Neck", 80, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
- -- Music of Bastion
- br.ui:createCheckbox(section, "Music of Bastion", "|cffFFFFFFCheck to use.")
- -- Phial of Serenity
- br.ui:createSpinner(section, "Phial of Serenity", 30, 0, 80, 5, "|cffFFFFFFHealth Percent to Cast At")
- br.ui:createCheckbox(section, "Auto Summon Steward")
+ -- -- Music of Bastion
+ -- br.ui:createCheckbox(section, "Music of Bastion", "|cffFFFFFFCheck to use.")
+ -- -- Phial of Serenity
+ -- br.ui:createSpinner(section, "Phial of Serenity", 30, 0, 80, 5, "|cffFFFFFFHealth Percent to Cast At")
+ -- br.ui:createCheckbox(section, "Auto Summon Steward")
end
-- Abilities - Call, module.BasicHealing(), in your rotation to use these
if section == nil then
-- Health Potion / Stones
- if getOption("Healthstone/Potion", "Check") and unit.inCombat() and unit.hp() <= getOption("Healthstone/Potion", "Value") then
+ if ui.checked("Healthstone/Potion") and unit.inCombat() and unit.hp() <= ui.value("Healthstone/Potion") then
--Health Pot should be first since it's the greatest heal; healthstones second
local healPot = BestHealingPotion()
- if healPot ~= nil and has.item(healPot) and br.canUseItem(healPot) then
+ if healPot ~= nil and has.item(healPot) and br.functions.item:canUseItem(healPot) then
if use.item(healPot) then
- ui.debug("using Healing Potion")
+ ui.debug("Using Healing Potion")
return true
end
end
@@ -157,15 +158,15 @@ br.api.module = function(self)
end
end
--Legion Healthstone
- if use.able.legionHealthstone() and has.legionHealthstone() then
- if use.legionHealthstone() then
- ui.debug("Using Legion Healthstone")
- return true
- end
- end
+ -- if use.able.legionHealthstone() and has.legionHealthstone() then
+ -- if use.legionHealthstone() then
+ -- ui.debug("Using Legion Healthstone")
+ -- return true
+ -- end
+ -- end
end
-- Heirloom Neck
- if getOption("Heirloom Neck", "Check") and unit.hp() <= getOption("Heirloom Neck", "Value") and not unit.inCombat() then
+ if ui.checked("Heirloom Neck") and unit.hp() <= ui.value("Heirloom Neck") and not unit.inCombat() then
if use.able.heirloomNeck() and item.heirloomNeck ~= 0 and item.heirloomNeck ~= item.manariTrainingAmulet then
if use.heirloomNeck() then
ui.debug("Using Heirloom Neck")
@@ -174,62 +175,62 @@ br.api.module = function(self)
end
end
-- Gift of the Naaru
- if getOption("Gift of the Naaru", "Check") and unit.race() == "Draenei"
- and unit.inCombat() and unit.hp() <= getOption("Gift of the Naaru", "Value")
+ if ui.checked("Gift of the Naaru") and unit.race() == "Draenei"
+ and unit.inCombat() and unit.hp() <= ui.value("Gift of the Naaru")
then
if cast.racial() then
ui.debug("Casting Gift of the Naaru")
return true
end
end
- -- Music of Bastion
- if getOption("Music of Bastion", "Check") and (br.isInArdenweald() or br.isInBastion() or br.isInMaldraxxus() or br.isInRevendreth()) then
- if use.able.ascendedFlute() and has.ascendedFlute() then
- if use.ascendedFlute() then
- ui.debug("Using Ascended Flute")
- return true
- end
- end
- if use.able.benevolentGong() and has.benevolentGong() then
- if use.benevolentGong() then
- ui.debug("Using Benevolent Gong")
- return true
- end
- end
- if use.able.heavenlyDrum() and has.heavenlyDrum() then
- if use.heavenlyDrum() then
- ui.debug("Using Heavenly Drum")
- return true
- end
- end
- if use.able.kyrianBell() and has.kyrianBell() then
- if use.kyrianBell() then
- ui.debug("Using Kyrian Bell")
- return true
- end
- end
- if use.able.unearthlyChime() and has.unearthlyChime() then
- if use.unearthlyChime() then
- ui.debug("Using Unearthly Chime")
- return true
- end
- end
- end
- -- Phial of Serenity
- if getOption("Phial of Serenity", "Check") then
- if getOption("Auto Summon Steward", "Check") and not unit.inCombat() and not has.phialOfSerenity() and cast.able.summonSteward() then
- if cast.summonSteward() then
- ui.debug("Casting Call Steward")
- return true
- end
- end
- if unit.inCombat() and use.able.phialOfSerenity() and unit.hp() < getOption("Phial of Serenity", "Value") then
- if use.phialOfSerenity() then
- ui.debug("Using Phial of Serenity")
- return true
- end
- end
- end
+ -- -- Music of Bastion
+ -- if getOption("Music of Bastion", "Check") and (br.functions.misc:isInArdenweald() or br.functions.misc:isInBastion() or br.functions.misc:isInMaldraxxus() or br.functions.misc:isInRevendreth()) then
+ -- if use.able.ascendedFlute() and has.ascendedFlute() then
+ -- if use.ascendedFlute() then
+ -- ui.debug("Using Ascended Flute")
+ -- return true
+ -- end
+ -- end
+ -- if use.able.benevolentGong() and has.benevolentGong() then
+ -- if use.benevolentGong() then
+ -- ui.debug("Using Benevolent Gong")
+ -- return true
+ -- end
+ -- end
+ -- if use.able.heavenlyDrum() and has.heavenlyDrum() then
+ -- if use.heavenlyDrum() then
+ -- ui.debug("Using Heavenly Drum")
+ -- return true
+ -- end
+ -- end
+ -- if use.able.kyrianBell() and has.kyrianBell() then
+ -- if use.kyrianBell() then
+ -- ui.debug("Using Kyrian Bell")
+ -- return true
+ -- end
+ -- end
+ -- if use.able.unearthlyChime() and has.unearthlyChime() then
+ -- if use.unearthlyChime() then
+ -- ui.debug("Using Unearthly Chime")
+ -- return true
+ -- end
+ -- end
+ -- end
+ -- -- Phial of Serenity
+ -- if getOption("Phial of Serenity", "Check") then
+ -- if getOption("Auto Summon Steward", "Check") and not unit.inCombat() and not has.phialOfSerenity() and cast.able.summonSteward() then
+ -- if cast.summonSteward() then
+ -- ui.debug("Casting Call Steward")
+ -- return true
+ -- end
+ -- end
+ -- if unit.inCombat() and use.able.phialOfSerenity() and unit.hp() < getOption("Phial of Serenity", "Value") then
+ -- if use.phialOfSerenity() then
+ -- ui.debug("Using Phial of Serenity")
+ -- return true
+ -- end
+ -- end
+ -- end
end
end
@@ -246,10 +247,11 @@ br.api.module = function(self)
"|cffFFFFFFWhen to use Trinket 1 (Slot 14).")
end
if section == nil then
+ enemies.get(8)
if slotID ~= nil then
-- For use in rotation loop - pass slotID
if slotID == 13 or slotID == 14 then
- if use.able.slot(slotID) and ui.alwaysCdAoENever("Trinket " .. slotID - 12, 3, #br.getEnemies("player", 8)) then
+ if use.able.slot(slotID) and ui.alwaysCdAoENever("Trinket " .. slotID - 12, 3, #enemies.yards8) then
if use.slot(slotID) then
ui.debug("Using Trinket " .. slotID - 12)
return true
@@ -260,7 +262,7 @@ br.api.module = function(self)
-- If not used in rotation loop - loop here
for slotID = 13, 14 do
-- local useTrinket = (opValue == 1 or (opValue == 2 and (ui.useCDs() or ui.useAOE())) or (opValue == 3 and ui.useCDs()))
- if use.able.slot(slotID) and ui.alwaysCdAoENever("Trinket " .. slotID - 12, 3, #br.getEnemies("player", 8)) then
+ if use.able.slot(slotID) and ui.alwaysCdAoENever("Trinket " .. slotID - 12, 3, #enemies.yards8) then
if use.slot(slotID) then
ui.debug("Using Trinket " .. slotID - 12)
return true
@@ -275,7 +277,7 @@ br.api.module = function(self)
-- @function module.CombatPotionUp
-- @bool[opt] section If set will generate the options for this module in the Profile Options. Otherwise, will run the module.
module.CombatPotionUp = function(section)
- local potList = { "Pot Ultimate Power", "Pot of Power" }
+ local potList = { "Virmen's Bite" }
if section ~= nil then
br.ui:createDropdown(section, "Use Combat Potion", potList, 1,
"|cffFFFFFFSelect Combat Potion, uses best quality.")
@@ -283,18 +285,18 @@ br.api.module = function(self)
if section == nil then
if not getOption("Use Combat Potion", "Check") then return false end
local opValue = getOption("Use Combat Potion", "Value")
- if opValue == 1 and use.isOneOfUsable(br.lists.items.elementalPotionOfUltimatePowerQualities) then
- if use.bestItem(br.lists.items.elementalPotionOfUltimatePowerQualities) then
- ui.debug("Using Best Pot: Elemental Potion of Ultimate Power")
- return true;
- end
- end
- if opValue == 2 and use.isOneOfUsable(br.lists.items.elementalPotionOfPowerQualities) then
- if use.bestItem(br.lists.items.elementalPotionOfPowerQualities) then
- ui.debug("Using Best Pot: Elemental Potion of Power")
+ if opValue == 1 and unit.instance("raid") and use.able.virmensBite() and not buff.virmensBite.exists() then
+ if use.virmensBite() then
+ ui.debug("Using Virmen's Bite")
return true;
end
end
+ -- if opValue == 2 and use.isOneOfUsable(br.lists.items.elementalPotionOfPowerQualities) then
+ -- if use.bestItem(br.lists.items.elementalPotionOfPowerQualities) then
+ -- ui.debug("Using Best Pot: Elemental Potion of Power")
+ -- return true;
+ -- end
+ -- end
end
end
@@ -313,33 +315,349 @@ br.api.module = function(self)
if buff.phialOfTepidVersatility.exists() then buff.phialOfTepidVersatility.cancel() end;
end
if section == nil then
- local opValue = getOption("Use DF Phial", "Value")
- if opValue == 1 and not buff.icedPhialOfCorruptingRage.exists() and use.isOneOfUsable(br.lists.items.icedPhialOfCorruptingRageQualities) then
- cancelBuffs()
- if use.bestItem(br.lists.items.icedPhialOfCorruptingRageQualities) then
- ui.debug("Using Best Phial: Iced Phial of Corrupting Rage")
- return true;
+ -- local opValue = getOption("Use DF Phial", "Value")
+ -- if opValue == 1 and not buff.icedPhialOfCorruptingRage.exists() and use.isOneOfUsable(br.lists.items.icedPhialOfCorruptingRageQualities) then
+ -- cancelBuffs()
+ -- if use.bestItem(br.lists.items.icedPhialOfCorruptingRageQualities) then
+ -- ui.debug("Using Best Phial: Iced Phial of Corrupting Rage")
+ -- return true;
+ -- end
+ -- end
+ -- if opValue == 2 and not buff.phialOfGlacialFury.exists() and use.isOneOfUsable(br.lists.items.phialOfGlacialFuryQualities) then
+ -- cancelBuffs()
+ -- if use.bestItem(br.lists.items.phialOfGlacialFuryQualities) then
+ -- ui.debug("Using Best Phial: Phial of Glacial Fury")
+ -- return true;
+ -- end
+ -- end
+ -- if opValue == 3 then
+ -- if not buff.phialOfTepidVersatility.exists() then
+ -- if use.isOneOfUsable(br.lists.items.phialOfTepidVersatilityQualities) then
+ -- cancelBuffs()
+ -- if use.bestItem(br.lists.items.phialOfTepidVersatilityQualities) then
+ -- ui.debug("Using Best Phial: Phial of Tepid Versatility")
+ -- return true;
+ -- end;
+ -- end
+ -- end
+ -- end
+ end
+ end
+
+ --- Racial Module - Attempts to use the racial ability based on the options set in the Profile Options.
+ --- @function module.Racial
+ --- @bool[opt] section If set will generate the options for this module in the Profile Options. Otherwise, will run the module.
+ --- @unit[opt] thisUnit If set will use the racial on the specified unit. Otherwise, will use on player or appropriate target.
+ module.Racial = function(section,thisUnit)
+ local race = unit.race()
+ local alwaysCdAoENever = { "Always", "|cff008000AOE", "|cffffff00AOE/CD", "|cff0000ffCD", "|cffff0000Never" }
+ -- local racialNames = {
+ -- Draenei = "Gift of the Naaru", -- Heal
+ -- Human = "Will To Survive", -- Loss of Control Break
+ -- Dwarf = "Stoneform", -- Decurse (Poison, Disease, Bleed) / Damage Reduction
+ -- NightElf = "Shadowmeld", -- Stealth
+ -- Gnome = "Escape Artist", -- Snare Removal
+ -- Orc = "Blood Fury", -- Damage Increase
+ -- Troll = "Berserking", -- Attack Speed Increase
+ -- BloodElf = "Arcane Torrent", -- Silence
+ -- Tauren = "War Stomp", -- AoE Stun
+ -- Undead = "Will of the Forsaken", -- Fear Break
+ -- Goblin = "Rocket Barrage", -- AoE Damage
+ -- Pandaren = "Quaking", -- AoE Stun
+ -- Worgen = "Darkflight" -- Movement Speed Increase
+ -- }
+
+ -- Blood Elf Racial (Silence + Resource)
+ if race == "BloodElf" then
+ if section ~= nil then
+ br.ui:createCheckbox(section, "Use Racial", "|cffFFFFFFAuto-use Arcane Torrent for interrupt/resource")
+ br.ui:createSpinner(section, "Use Racial Resource", 80, 0, 100, 5, "|cffFFFFFFResource Percent to Cast At")
+ return
+ end
+ if section == nil then
+ if not getOption("Use Racial", "Check") then return false end
+ local needsResource = unit.power("player") <= getOption("Use Racial Resource", "Value")
+ local canInterrupt = unit.casting() or unit.channeling()
+
+ if (needsResource or canInterrupt) and cast.able.racial() then
+ if cast.racial() then
+ ui.debug("Casting Racial - Arcane Torrent")
+ return true
+ end
end
+ return false
end
- if opValue == 2 and not buff.phialOfGlacialFury.exists() and use.isOneOfUsable(br.lists.items.phialOfGlacialFuryQualities) then
- cancelBuffs()
- if use.bestItem(br.lists.items.phialOfGlacialFuryQualities) then
- ui.debug("Using Best Phial: Phial of Glacial Fury")
- return true;
+ end
+
+ -- Draenei Racial (Healing)
+ if race == "Draenei" then
+ if section ~= nil then
+ br.ui:createSpinner(section, "Use Racial", 50, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ return
+ end
+ if section == nil then
+ if not getOption("Use Racial", "Check") then return false end
+ if unit.hp() > getOption("Use Racial", "Value") then return false end
+ if cast.able.racial() then
+ if cast.racial() then
+ ui.debug("Casting Racial - Gift of the Naaru")
+ return true
+ end
+ end
+ return false
+ end
+ end
+
+ -- Dwarf Racial (Decurse + Damage Reduction)
+ if race == "Dwarf" then
+ if section ~= nil then
+ br.ui:createSpinner(section, "Use Racial for Defense", 30, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ br.ui:createCheckbox(section, "Use Racial for Decurse", "|cffFFFFFFAuto-use Stoneform to remove poison/disease/bleed")
+ return
+ end
+ if section == nil then
+ if not getOption("Use Racial", "Check") then return false end
+ local useForHealth = unit.hp() <= getOption("Use Racial", "Value")
+ --local useForDebuffs = getOption("Use Racial for Decurse", "Check")
+ -- and (unit.hasDebuffType("poison") or unit.hasDebuffType("disease") or unit.hasDebuffType("bleed"))
+
+ if useForHealth--[[ (useForHealth or useForDebuffs)]] and cast.able.racial() then
+ if cast.racial() then
+ ui.debug("Casting Racial - Stoneform")
+ return true
+ end
+ end
+ return false
+ end
+ end
+
+ -- Gnome Racial (Snare Removal)
+ if race == "Gnome" then
+ if section ~= nil then
+ br.ui:createCheckbox(section, "Use Racial", "|cffFFFFFFAuto-use Escape Artist when slowed/rooted")
+ return
+ end
+ if section == nil then
+ if not getOption("Use Racial", "Check") then return false end
+ if --[[unit.isSlowed("player") and ]]cast.able.racial() then
+ if cast.racial() then
+ ui.debug("Casting Racial - Escape Artist")
+ return true
+ end
+ end
+ return false
+ end
+ end
+
+ -- Goblin Racial (AoE Damage)
+ if race == "Goblin" then
+ if section ~= nil then
+ br.ui:createCheckbox(section, "Use Racial", "|cffFFFFFFAuto-use Rocket Barrage during combat")
+ br.ui:createSpinner(section, "Use Racial Enemies", 1, 1, 10, 1, "|cffFFFFFFMinimum enemies nearby to use")
+ return
+ end
+ if section == nil then
+ if not getOption("Use Racial", "Check") then return false end
+ local enemyCount = #br.engines.enemiesEngineFunctions:getEnemies("player", 30) >= getOption("Use Racial Enemies", "Value")
+
+ if unit.inCombat() and enemyCount and cast.able.racial() then
+ if cast.racial() then
+ ui.debug("Casting Racial - Rocket Barrage")
+ return true
+ end
+ end
+ return false
+ end
+ end
+
+ -- Human Racial (Loss of Control Break)
+ if race == "Human" then
+ if section ~= nil then
+ br.ui:createCheckbox(section, "Use Racial", "|cffFFFFFFAuto-use Will to Survive when feared/charmed/incapacitated")
+ return
+ end
+ if section == nil then
+ if not getOption("Use Racial", "Check") then return false end
+ if --[[unit.isCC("player") and ]]cast.able.racial() then
+ if cast.racial() then
+ ui.debug("Casting Racial - Will to Survive")
+ return true
+ end
+ end
+ return false
+ end
+ end
+
+ -- Night Elf Racial (Stealth)
+ if race == "NightElf" then
+ if section ~= nil then
+ br.ui:createSpinner(section, "Use Racial", 30, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ br.ui:createCheckbox(section, "Use Racial in Combat", "|cffFFFFFFAllow Shadowmeld use during combat")
+ return
+ end
+ if section == nil then
+ if not getOption("Use Racial", "Check") then return false end
+ local lowHealth = unit.hp() <= getOption("Use Racial", "Value")
+ local allowInCombat = getOption("Use Racial in Combat", "Check")
+
+ if lowHealth and (not unit.inCombat() or allowInCombat) and cast.able.racial() then
+ if cast.racial() then
+ ui.debug("Casting Racial - Shadowmeld")
+ return true
+ end
+ end
+ return false
+ end
+ end
+
+ -- Orc Racial (Damage Increase)
+ if race == "Orc" then
+ if section ~= nil then
+ br.ui:createDropdownWithout(section, "Use Racial", alwaysCdAoENever, 3, "|cffFFFFFFWhen to use Racial")
+ return
+ end
+ if section == nil then
+ if not getOption("Use Racial", "Check") then return false end
+ if unit.inCombat() and cast.able.racial() then
+ if cast.racial() then
+ ui.debug("Casting Racial - Blood Fury")
+ return true
+ end
+ end
+ return false
+ end
+ end
+
+ -- Pandaren Racial (AoE Stun)
+ if race == "Pandaren" then
+ if section ~= nil then
+ br.ui:createCheckbox(section, "Use Racial")
+ return
+ end
+ if section == nil then
+ if not getOption("Use Racial", "Check") then return false end
+ if cast.able.racial(thisUnit) then
+ if cast.racial(thisUnit) then
+ ui.debug("Casting Racial on "..unit.name(thisUnit).." - Quaking Palm [Interrupt]")
+ return true
+ end
+ end
+ return false
+ end
+ end
+
+ -- Tauren Racial (AoE Stun)
+ if race == "Tauren" then
+ if section ~= nil then
+ br.ui:createSpinner(section, "Use Racial", 30, 0, 100, 5, "|cffFFFFFFHealth Percent to Cast At")
+ br.ui:createSpinner(section, "Use Racial Enemies", 2, 1, 10, 1, "|cffFFFFFFMinimum enemies nearby to use")
+ return
+ end
+ if section == nil then
+ if not getOption("Use Racial", "Check") then return false end
+ local lowHealth = unit.hp() <= getOption("Use Racial", "Value")
+ local enemyCount = #br.engines.enemiesEngineFunctions:getEnemies("player", 8) >= getOption("Use Racial Enemies", "Value")
+
+ if (lowHealth or enemyCount) and cast.able.racial() then
+ if cast.racial() then
+ ui.debug("Casting Racial - War Stomp")
+ return true
+ end
+ end
+ return false
+ end
+ end
+
+ -- Troll Racial (Attack Speed Increase)
+ if race == "Troll" then
+ if section ~= nil then
+ br.ui:createDropdownWithout(section, "Use Racial", alwaysCdAoENever, 3, "|cffFFFFFFWhen to use Racial")
+ return
+ end
+ if section == nil then
+ if not getOption("Use Racial", "Check") then return false end
+ if unit.inCombat() and cast.able.racial() then
+ if cast.racial() then
+ ui.debug("Casting Racial - Berserking")
+ return true
+ end
end
+ return false
+ end
+ end
+
+ -- Undead Racial (Fear Break)
+ if race == "Undead" then
+ if section ~= nil then
+ br.ui:createCheckbox(section, "Use Racial", "|cffFFFFFFAuto-use Will of the Forsaken when feared/charmed")
+ return
+ end
+ if section == nil then
+ if not getOption("Use Racial", "Check") then return false end
+ if --[[(unit.hasDebuffType("fear") or unit.hasDebuffType("charm")) and]] cast.able.racial() then
+ if cast.racial() then
+ ui.debug("Casting Racial - Will of the Forsaken")
+ return true
+ end
+ end
+ return false
+ end
+ end
+
+ -- Worgen Racial (Movement Speed)
+ if race == "Worgen" then
+ if section ~= nil then
+ br.ui:createCheckbox(section, "Use Racial", "|cffFFFFFFAuto-use Darkflight for movement")
+ return
end
- if opValue == 3 then
- if not buff.phialOfTepidVersatility.exists() then
- if use.isOneOfUsable(br.lists.items.phialOfTepidVersatilityQualities) then
- cancelBuffs()
- if use.bestItem(br.lists.items.phialOfTepidVersatilityQualities) then
- ui.debug("Using Best Phial: Phial of Tepid Versatility")
- return true;
- end;
+ if section == nil then
+ if not getOption("Use Racial", "Check") then return false end
+ if unit.moving() --[[and not unit.hasMovementBuff() ]]and cast.able.racial() then
+ if cast.racial() then
+ ui.debug("Casting Racial - Darkflight")
+ return true
end
end
+ return false
end
end
+
+ return false
+ end
+
+ --- Two Forms Module - Return Worgen to Human Form out of combat
+ -- @function module.TwoForms
+ -- @bool[opt] section If set will generate the options for this module in the Profile Options. Otherwise, will run the module.
+ local twoFormsDone = false
+ module.TwoForms = function(section)
+ if section ~= nil then
+ br.ui:createCheckbox(section, "Two Forms", "|cffFFFFFFReturn to Human Form when out of combat (Worgen only)")
+ return
+ end
+ if section == nil then
+ if not getOption("Two Forms", "Check") then return false end
+
+ -- When entering combat reset the flag so we can attempt again after combat ends.
+ if unit.inCombat() then
+ if twoFormsDone then
+ ui.debug("Resetting Two Forms flag to false (entered combat)")
+ twoFormsDone = false
+ end
+ else
+ -- Out of combat: attempt to return to Human Form once
+ if unit.race() == "Worgen" and not unit.mounted() and not twoFormsDone
+ and cast.able.twoForms() and unit.gcd() == 0
+ then
+ if cast.twoForms() then
+ ui.debug("Casting Two Forms - Returning to Human Form")
+ twoFormsDone = true
+ return true
+ end
+ end
+ end
+
+ return false
+ end
end
--- ImbueUp Module - Attmpts to use the weapon imbuement specified in the Profile Options
@@ -354,16 +672,16 @@ br.api.module = function(self)
if getOption("Weapon Imbuement", "Check") then
local selValue = getOption("Weapon Imbuement", "Value")
local auras = {}
- if selValue == 1 then auras = br.lists.spells.Shared.Shared.itemEnchantments.buzzingRune end
- if selValue == 2 then auras = br.lists.spells.Shared.Shared.itemEnchantments.chirpingRune end
- if selValue == 3 then auras = br.lists.spells.Shared.Shared.itemEnchantments.howlingRune end
- if selValue == 4 then auras = br.lists.spells.Shared.Shared.itemEnchantments.hissingRune end
- if not unit.weaponImbue.exists(auras) then
- if selValue == 1 then return use.bestItem(br.lists.items.buzzingRuneQualities) end
- if selValue == 2 then return use.bestItem(br.lists.items.chirpingRuneQualities) end
- if selValue == 3 then return use.bestItem(br.lists.items.howlingRuneQualities) end
- if selValue == 4 then return use.bestItem(br.lists.items.hissingRuneQualities) end
- end
+ -- if selValue == 1 then auras = br.lists.spells.Shared.Shared.itemEnchantments.buzzingRune end
+ -- if selValue == 2 then auras = br.lists.spells.Shared.Shared.itemEnchantments.chirpingRune end
+ -- if selValue == 3 then auras = br.lists.spells.Shared.Shared.itemEnchantments.howlingRune end
+ -- if selValue == 4 then auras = br.lists.spells.Shared.Shared.itemEnchantments.hissingRune end
+ -- if not unit.weaponImbue.exists(auras) then
+ -- if selValue == 1 then return use.bestItem(br.lists.items.buzzingRuneQualities) end
+ -- if selValue == 2 then return use.bestItem(br.lists.items.chirpingRuneQualities) end
+ -- if selValue == 3 then return use.bestItem(br.lists.items.howlingRuneQualities) end
+ -- if selValue == 4 then return use.bestItem(br.lists.items.hissingRuneQualities) end
+ -- end
end
end
end
@@ -375,7 +693,7 @@ br.api.module = function(self)
module.FlaskUp = function(buffType, section)
local function getFlaskByType(buff)
local thisFlask = ""
- if buff == "Agility" then thisFlask = "Greater Flask of the Currents" end
+ if buff == "Agility" then thisFlask = "Flask of Spring Blossoms" end
if buff == "Intellect" then thisFlask = "Greater Flask of Endless Fathoms" end
if buff == "Stamina" then thisFlask = "Greater Flask of the Vast Horizon" end
if buff == "Strength" then thisFlask = "Greater Flask of the Undertow" end
@@ -398,35 +716,35 @@ br.api.module = function(self)
local function hasFlaskBuff()
local flask = getOption("Flask", "Value")
if flask == 2 then -- Greater Flask
- return buff.greaterFlaskOfTheCurrents.exists() or buff.greaterFlaskOfEndlessFathoms.exists() or
- buff.greaterFlaskOfTheVastHorizon.exists() or buff.greaterFlaskOfTheUndertow.exists()
- end
- if flask == 3 then
- if isDH then -- Greater FLask or Gaze of the Legion
- return buff.greaterFlaskOfTheCurrents.exists() or buff.greaterFlaskOfEndlessFathoms.exists() or
- buff.greaterFlaskOfTheVastHorizon.exists()
- or buff.greaterFlaskOfTheUndertow.exists() or buff.gazeOfTheLegion.exists()
- else -- Greater Flask or Fel Focus
- return buff.greaterFlaskOfTheCurrents.exists() or buff.greaterFlaskOfEndlessFathoms.exists() or
- buff.greaterFlaskOfTheVastHorizon.exists()
- or buff.greaterFlaskOfTheUndertow.exists() or buff.felFocus.exists()
- end
- end
- if flask == 4 and isDH then -- DH - Greater Flask or Gaze of the Legion or Fel Focus
- return buff.greaterFlaskOfTheCurrents.exists() or buff.greaterFlaskOfEndlessFathoms.exists() or
- buff.greaterFlaskOfTheVastHorizon.exists()
- or buff.greaterFlaskOfTheUndertow.exists() or buff.gazeOfTheLegion.exists() or buff.felFocus.exists()
+ return buff.flaskOfSpringBlossoms.exists() --or buff.greaterFlaskOfEndlessFathoms.exists() or
+ --buff.greaterFlaskOfTheVastHorizon.exists() or buff.greaterFlaskOfTheUndertow.exists()
end
+ -- if flask == 3 then
+ -- if isDH then -- Greater FLask or Gaze of the Legion
+ -- return buff.greaterFlaskOfTheCurrents.exists() or buff.greaterFlaskOfEndlessFathoms.exists() or
+ -- buff.greaterFlaskOfTheVastHorizon.exists()
+ -- or buff.greaterFlaskOfTheUndertow.exists() or buff.gazeOfTheLegion.exists()
+ -- else -- Greater Flask or Fel Focus
+ -- return buff.greaterFlaskOfTheCurrents.exists() or buff.greaterFlaskOfEndlessFathoms.exists() or
+ -- buff.greaterFlaskOfTheVastHorizon.exists()
+ -- or buff.greaterFlaskOfTheUndertow.exists() or buff.felFocus.exists()
+ -- end
+ -- end
+ -- if flask == 4 and isDH then -- DH - Greater Flask or Gaze of the Legion or Fel Focus
+ -- return buff.greaterFlaskOfTheCurrents.exists() or buff.greaterFlaskOfEndlessFathoms.exists() or
+ -- buff.greaterFlaskOfTheVastHorizon.exists()
+ -- or buff.greaterFlaskOfTheUndertow.exists() or buff.gazeOfTheLegion.exists() or buff.felFocus.exists()
+ -- end
end
local function cancelFlaskBuff()
- if buff.greaterFlaskOfTheCurrents.exists() then buff.greaterFlaskOfTheCurrents.cancel() end
- if buff.greaterFlaskOfEndlessFathoms.exists() then buff.greaterFlaskOfEndlessFathoms.cancel() end
- if buff.greaterFlaskOfTheVastHorizon.exists() then buff.greaterFlaskOfTheVastHorizon.cancel() end
- if buff.greaterFlaskOfTheUndertow.exists() then buff.greaterFlaskOfTheUndertow.cancel() end
- if (isDH and buff.gazeOfTheLegion.exists()) then buff.gazeOfTheLegion.cancel() end
- if buff.felFocus.exists() then buff.felFocus.cancel() end
- if buff.whispersOfInsanity.exists() then buff.whispersOfInsanity.cancel() end
+ if buff.flaskOfSpringBlossoms.exists() then buff.flaskOfSpringBlossoms.cancel() end
+ -- if buff.greaterFlaskOfEndlessFathoms.exists() then buff.greaterFlaskOfEndlessFathoms.cancel() end
+ -- if buff.greaterFlaskOfTheVastHorizon.exists() then buff.greaterFlaskOfTheVastHorizon.cancel() end
+ -- if buff.greaterFlaskOfTheUndertow.exists() then buff.greaterFlaskOfTheUndertow.cancel() end
+ -- if (isDH and buff.gazeOfTheLegion.exists()) then buff.gazeOfTheLegion.cancel() end
+ -- if buff.felFocus.exists() then buff.felFocus.cancel() end
+ -- if buff.whispersOfInsanity.exists() then buff.whispersOfInsanity.cancel() end
end
@@ -437,75 +755,75 @@ br.api.module = function(self)
local opValue = getOption("Flask", "Value")
local thisFlask = getFlaskByType(buffType)
if opValue == 1 and unit.instance("raid") then
- if thisFlask == "Greater Flask of the Currents" and use.able.greaterFlaskOfTheCurrents() and not buff.greaterFlaskOfTheCurrents.exists() then
- cancelFlaskBuff()
- if use.greaterFlaskOfTheCurrents() then
- ui.debug("Using Greater Flask of the Currents")
- return true
- end
- end
- if thisFlask == "Greater Flask of Endless Fathoms" and use.able.greaterFlaskOfEndlessFathoms() and not buff.greaterFlaskOfEndlessFathoms.exists() then
+ if thisFlask == "Flask of Spring Blossoms" and use.able.flaskOfSpringBlossoms() and not buff.flaskOfSpringBlossoms.exists() then
cancelFlaskBuff()
- if use.greaterFlaskOfEndlessFathoms() then
- ui.debug("Using Greater Flask of Endless Fathoms")
- return true
- end
- end
- if thisFlask == "Greater Flask of the Vast Horizon" and use.able.greaterFlaskOfTheVastHorizon() and not buff.greaterFlaskOfTheVastHorizon.exists() then
- cancelFlaskBuff()
- if use.greaterFlaskOfTheVastHorizon() then
- ui.debug("Using Greater Flask of the Vast Horizon")
- return true
- end
- end
- if thisFlask == "Greater Flask of the Undertow" and use.able.greaterFlaskOfTheUndertow() and not buff.greaterFlaskOfTheUndertow.exists() then
- cancelFlaskBuff()
- if use.greaterFlaskOfTheUndertow() then
- ui.debug("Using Greater Flask of the Undertow")
+ if use.flaskOfSpringBlossoms() then
+ ui.debug("Using Flask of Spring Blossoms")
return true
end
end
+ -- if thisFlask == "Greater Flask of Endless Fathoms" and use.able.greaterFlaskOfEndlessFathoms() and not buff.greaterFlaskOfEndlessFathoms.exists() then
+ -- cancelFlaskBuff()
+ -- if use.greaterFlaskOfEndlessFathoms() then
+ -- ui.debug("Using Greater Flask of Endless Fathoms")
+ -- return true
+ -- end
+ -- end
+ -- if thisFlask == "Greater Flask of the Vast Horizon" and use.able.greaterFlaskOfTheVastHorizon() and not buff.greaterFlaskOfTheVastHorizon.exists() then
+ -- cancelFlaskBuff()
+ -- if use.greaterFlaskOfTheVastHorizon() then
+ -- ui.debug("Using Greater Flask of the Vast Horizon")
+ -- return true
+ -- end
+ -- end
+ -- if thisFlask == "Greater Flask of the Undertow" and use.able.greaterFlaskOfTheUndertow() and not buff.greaterFlaskOfTheUndertow.exists() then
+ -- cancelFlaskBuff()
+ -- if use.greaterFlaskOfTheUndertow() then
+ -- ui.debug("Using Greater Flask of the Undertow")
+ -- return true
+ -- end
+ -- end
end
if opValue == 2 and (not unit.instance("raid") or (unit.instance("raid") and not hasFlaskBuff())) then
- if isDH and use.able.inquisitorsMenacingEye() and not buff.gazeOfTheLegion.exists() then
- cancelFlaskBuff()
- if use.inquisitorsMenacingEye() then
- ui.debug("Using Inquisitor's Menacing Eye")
- return true
- end
- elseif use.able.repurposedFelFocuser() and not buff.felFocus.exists() then
- cancelFlaskBuff()
- if use.repurposedFelFocuser() then
- ui.debug("Using Repurposed Fel Focuser")
- return true
- end
- end
+ -- if isDH and use.able.inquisitorsMenacingEye() and not buff.gazeOfTheLegion.exists() then
+ -- cancelFlaskBuff()
+ -- if use.inquisitorsMenacingEye() then
+ -- ui.debug("Using Inquisitor's Menacing Eye")
+ -- return true
+ -- end
+ -- elseif use.able.repurposedFelFocuser() and not buff.felFocus.exists() then
+ -- cancelFlaskBuff()
+ -- if use.repurposedFelFocuser() then
+ -- ui.debug("Using Repurposed Fel Focuser")
+ -- return true
+ -- end
+ -- end
end
if opValue == 3 and (not unit.instance("raid") or (unit.instance("raid") and not hasFlaskBuff())) then
- if isDH and use.able.repurposedFelFocuser() and not buff.felFocus.exists() then
- cancelFlaskBuff()
- if use.repurposedFelFocuser() then
- ui.debug("Using Repurposed Fel Focuser")
- return true
- end
- elseif use.able.oraliusWhisperingCrystal() and not buff.whispersOfInsanity.exists() then
- cancelFlaskBuff()
- if use.oraliusWhisperingCrystal() then
- ui.debug("Using Oralius's Whispering Crystal")
- return true
- end
- end
+ -- if isDH and use.able.repurposedFelFocuser() and not buff.felFocus.exists() then
+ -- cancelFlaskBuff()
+ -- if use.repurposedFelFocuser() then
+ -- ui.debug("Using Repurposed Fel Focuser")
+ -- return true
+ -- end
+ -- elseif use.able.oraliusWhisperingCrystal() and not buff.whispersOfInsanity.exists() then
+ -- cancelFlaskBuff()
+ -- if use.oraliusWhisperingCrystal() then
+ -- ui.debug("Using Oralius's Whispering Crystal")
+ -- return true
+ -- end
+ -- end
end
if opValue == 4 then
- if isDH and (not unit.instance("raid") or (unit.instance("raid") and not hasFlaskBuff()))
- and use.able.oraliusWhisperingCrystal() and not buff.whispersOfInsanity.exists()
- then
- cancelFlaskBuff()
- if use.oraliusWhisperingCrystal() then
- ui.debug("Using Oralius's Whispering Crystal")
- return true
- end
- end
+ -- if isDH and (not unit.instance("raid") or (unit.instance("raid") and not hasFlaskBuff()))
+ -- and use.able.oraliusWhisperingCrystal() and not buff.whispersOfInsanity.exists()
+ -- then
+ -- cancelFlaskBuff()
+ -- if use.oraliusWhisperingCrystal() then
+ -- ui.debug("Using Oralius's Whispering Crystal")
+ -- return true
+ -- end
+ -- end
end
end
end
diff --git a/System/API/Pet.lua b/System/API/Pet.lua
index 2724d36d13..76b8965a55 100644
--- a/System/API/Pet.lua
+++ b/System/API/Pet.lua
@@ -18,7 +18,7 @@ br.api.pets = function(pet,k,v,brplayer)
-- @return boolean True if player has an active pet, false otherwise
-- @usage local hasPet = br.player.pet.active.exists()
pet.active.exists = function()
- return br.GetObjectExists("pet")
+ return br.functions.unit:GetObjectExists("pet")
end
---
@@ -29,7 +29,7 @@ br.api.pets = function(pet,k,v,brplayer)
local count = 0
for l,_ in pairs(brplayer.pet.list) do
local listID = brplayer.pet.list[l].id
- if br.GetObjectID("pet") == listID then count = count + 1 end
+ if br.functions.unit:GetObjectID("pet") == listID then count = count + 1 end
end
return count
end
@@ -39,7 +39,7 @@ br.api.pets = function(pet,k,v,brplayer)
-- @function br.player.pet.active.id
-- @return number The ID of the active pet
pet.active.id = function()
- return br.GetObjectID("pet")
+ return br.functions.unit:GetObjectID("pet")
end
end
pet = petList
@@ -89,7 +89,7 @@ br.api.pets = function(pet,k,v,brplayer)
-- self.pet.buff.exists = function(buffID,petID)
-- for k, v in pairs(self.pet) do
-- local pet = self.pet[k]
- -- if self.pet[k].id == petID and br.UnitBuffID(k,buffID) ~= nil then return true end
+ -- if self.pet[k].id == petID and br.functions.aura:UnitBuffID(k,buffID) ~= nil then return true end
-- end
-- return false
-- end
@@ -98,7 +98,7 @@ br.api.pets = function(pet,k,v,brplayer)
-- local petCount = 0
-- for k, v in pairs(self.pet) do
-- local pet = self.pet[k]
- -- if self.pet[k].id == petID and br.UnitBuffID(k,buffID) ~= nil then petCount = petCount + 1 end
+ -- if self.pet[k].id == petID and br.functions.aura:UnitBuffID(k,buffID) ~= nil then petCount = petCount + 1 end
-- end
-- return petCount
-- end
@@ -107,7 +107,7 @@ br.api.pets = function(pet,k,v,brplayer)
-- local petCount = 0
-- for k, v in pairs(self.pet) do
-- local pet = self.pet[k]
- -- if self.pet[k].id == petID and br.UnitBuffID(k,buffID) == nil then petCount = petCount + 1 end
+ -- if self.pet[k].id == petID and br.functions.aura:UnitBuffID(k,buffID) == nil then petCount = petCount + 1 end
-- end
-- return petCount
-- end
diff --git a/System/API/Power.lua b/System/API/Power.lua
index 42debbb153..c0d7d49529 100644
--- a/System/API/Power.lua
+++ b/System/API/Power.lua
@@ -31,13 +31,13 @@ if br.api == nil then br.api = {} end
br.api.power = function(power,powerType,powerIndex)
local isDKRunes = select(2,br._G.UnitClass("player")) == "DEATHKNIGHT" and powerIndex == 5
- local isDestruction = br._G.GetSpecializationInfo(br._G.GetSpecialization()) == 267 and powerIndex == 7
+ local isDestruction = br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization()) == 267 and powerIndex == 7
--- Gets the amount of specified power until max is reached
-- @function power.powerType.deficit
-- @return number
power[powerType].deficit = function()
- return br.getPowerMax("player",powerIndex) - br.getPower("player",powerIndex)
+ return br.functions.power:getPowerMax("player",powerIndex) - br.functions.power:getPower("player",powerIndex)
end
--- Gets the fractional counts of their available runes - Used by DKs
@@ -46,11 +46,7 @@ br.api.power = function(power,powerType,powerIndex)
power[powerType].frac = function()
-- Death Knights
if isDKRunes then
- local runeCount = 0
- for i = 1, 6 do
- runeCount = runeCount + br._G.GetRuneCount(i)
- end
- return runeCount + math.max(br.runeCDPercent(1),br.runeCDPercent(2),br.runeCDPercent(3),br.runeCDPercent(4),br.runeCDPercent(5),br.runeCDPercent(6))
+ return br.functions.power:getRuneFrac()
end
-- Destruction Warlocks
if isDestruction then
@@ -59,24 +55,24 @@ br.api.power = function(power,powerType,powerIndex)
local fragmentCount = (shardModifier ~= 0) and (shardPower / shardModifier) or 0
return (shardPower + fragmentCount)/10
end
- return br.getPower("player",powerIndex)
+ return br.functions.power:getPower("player",powerIndex)
end
--- Gets the maximum aount of the specified power
-- @function power.powerType.max
-- @return number
power[powerType].max = function()
- return br.getPowerMax("player",powerIndex)
+ return br.functions.power:getPowerMax("player",powerIndex)
end
--- Gets the current amount of specified power as a percentage
-- @function power.powerType.percent
-- @return number
power[powerType].percent = function()
- if br.getPowerMax("player",powerIndex) == 0 then
+ if br.functions.power:getPowerMax("player",powerIndex) == 0 then
return 0
else
- return ((br.getPower("player",powerIndex) / br.getPowerMax("player",powerIndex)) * 100)
+ return ((br.functions.power:getPower("player",powerIndex) / br.functions.power:getPowerMax("player",powerIndex)) * 100)
end
end
@@ -84,7 +80,7 @@ br.api.power = function(power,powerType,powerIndex)
-- @function power.powerType.regen
-- @return number
power[powerType].regen = function()
- return br.getRegen("player")
+ return br.functions.power:getRegen("player")
end
--- Gets the time in seconds until the specified power is maxed
@@ -93,9 +89,9 @@ br.api.power = function(power,powerType,powerIndex)
power[powerType].ttm = function(amount)
if isDKRunes then
if amount == nil then amount = 6 end
- return br.runeTimeTill(amount)
+ return br.functions.power:runeTimeTill(amount)
else
- return br.getTimeToMax("player",amount)
+ return br.functions.power:getTimeToMax("player",amount)
end
end
@@ -103,11 +99,11 @@ br.api.power = function(power,powerType,powerIndex)
-- -- This function serves a dual purpose. When `isDKRunes` is true,
-- -- it counts the total number of 'runes' by executing a loop 6 times
-- -- and adding the result of `br._G.GetRuneCount(i)` to `runeCount` in each iteration.
- -- -- If `isDKRunes` is false, it simply retrieves the power of the player using `br.getPower()`.
+ -- -- If `isDKRunes` is false, it simply retrieves the power of the player using `br.functions.power:getPower()`.
-- -- @global
-- -- @name power[powerType]
-- -- @class function
- -- -- @return runeCount if `isDKRunes` is true, otherwise it returns the result of `br.getPower("player",powerIndex)`
+ -- -- @return runeCount if `isDKRunes` is true, otherwise it returns the result of `br.functions.power:getPower("player",powerIndex)`
-- -- power[powerType] = function()
-- -- if isDKRunes then
-- -- local runeCount = 0
@@ -116,7 +112,7 @@ br.api.power = function(power,powerType,powerIndex)
-- -- end
-- -- return runeCount
-- -- else
- -- -- return br.getPower("player",powerIndex)
+ -- -- return br.functions.power:getPower("player",powerIndex)
-- -- end
-- -- end
@@ -127,26 +123,64 @@ br.api.power = function(power,powerType,powerIndex)
-- @return number
__call = function(tbl, ...)
if isDKRunes then
- local runeCount = 0
- for i = 1, 6 do
- runeCount = runeCount + br._G.GetRuneCount(i)
- end
- return runeCount
+ return br.functions.power:getTotalReadyRunes()
else
- return br.getPower("player",powerIndex)
+ local unit = powerIndex == 4 and "target" or "player"
+ return br.functions.power:getPower(unit,powerIndex)
end
end,
-- Define the __index metamethod
__index = function(tbl, key)
- -- Defer to the original table for existing methods
- if tbl[key] then
- return tbl[key]
+ -- Use rawget to avoid triggering __index recursively
+ local val = rawget(tbl, key)
+ if val ~= nil then
+ return val
end
- -- Handle any additional properties here if needed
+ -- No value found; return nil (or handle computed properties here)
+ return nil
end
}
-- Apply the metatable to your power object
setmetatable(power[powerType], powerMetaTable)
+ -- Convenience rune helpers for Death Knight (rune-type specific)
+ if isDKRunes then
+ power[powerType].runes = power[powerType].runes or {}
+ power[powerType].runes.count = function(rtype)
+ return br.functions.power:getRuneCount(rtype)
+ end
+ power[powerType].runes.percent = function(rtype)
+ return br.functions.power:getRunePercent(rtype)
+ end
+ power[powerType].runes.info = function()
+ return br.functions.power:getRuneObjects()
+ end
+ power[powerType].runes.recharge = function(index)
+ return br.functions.power:runeRecharge(index)
+ end
+ power[powerType].runes.cdPercent = function(index)
+ return br.functions.power:runeCDPercent(index)
+ end
+ power[powerType].runes.timeTill = function(n)
+ return br.functions.power:runeTimeTill(n)
+ end
+ power[powerType].runes.frost = function() return power[powerType].runes.count("frost") end
+ power[powerType].runes.unholy = function() return power[powerType].runes.count("unholy") end
+ power[powerType].runes.blood = function() return power[powerType].runes.count("blood") end
+ power[powerType].runes.death = function() return power[powerType].runes.count("death") end
+ power[powerType].runes.frostPercent = function() return power[powerType].runes.percent("frost") end
+ power[powerType].runes.unholyPercent = function() return power[powerType].runes.percent("unholy") end
+ power[powerType].runes.bloodPercent = function() return power[powerType].runes.percent("blood") end
+ power[powerType].runes.deathPercent = function() return power[powerType].runes.percent("death") end
+ -- Top-level aliases so callers can use br.player.power.runes.() directly
+ power[powerType].frost = function() return power[powerType].runes.frost() end
+ power[powerType].unholy = function() return power[powerType].runes.unholy() end
+ power[powerType].blood = function() return power[powerType].runes.blood() end
+ power[powerType].death = function() return power[powerType].runes.death() end
+ power[powerType].frostPercent = function() return power[powerType].runes.frostPercent() end
+ power[powerType].unholyPercent = function() return power[powerType].runes.unholyPercent() end
+ power[powerType].bloodPercent = function() return power[powerType].runes.bloodPercent() end
+ power[powerType].deathPercent = function() return power[powerType].runes.deathPercent() end
+ end
end
diff --git a/System/API/Spell.lua b/System/API/Spell.lua
index be8ed1439a..5a42e65421 100644
--- a/System/API/Spell.lua
+++ b/System/API/Spell.lua
@@ -18,7 +18,8 @@ br.api.spell = function(self, k, v)
-- @function spell.k.castTime
-- @return number
spell[k].castTime = function()
- local _, _, _, castTime = br._G.GetSpellInfo(v)
+ local spellID = type(v) == "table" and (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(v) or v[1]) or v
+ local _, _, _, castTime = br.api.wow.GetSpellInfo(spellID)
return castTime or 0
end
@@ -26,22 +27,24 @@ br.api.spell = function(self, k, v)
-- @function spell.k.id
-- @return number
spell[k].id = function()
- local _, _, _, _, _, _, spellID = br._G.GetSpellInfo(v)
- return spellID or 0
+ local spellID = type(v) == "table" and (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(v) or v[1]) or v
+ local _, _, _, _, _, _, realID = br.api.wow.GetSpellInfo(spellID)
+ return realID or 0
end
--- Checks if the spell is known.
-- @function spell.k.known
-- @return boolean
spell[k].known = function()
- return br.isKnown(v)
+ return br.functions.spell:isKnown(v)
end
--- Returns the spell's max range.
-- @function spell.k.maxRange
-- @return number
spell[k].maxRange = function()
- local _, _, _, _, _, maxRange = br._G.GetSpellInfo(v)
+ local spellID = type(v) == "table" and (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(v) or v[1]) or v
+ local _, _, _, _, _, maxRange = br.api.wow.GetSpellInfo(spellID)
return maxRange or 0
end
@@ -49,7 +52,8 @@ br.api.spell = function(self, k, v)
-- @function spell.k.minRange
-- @return number
spell[k].minRange = function()
- local _, _, _, _, minRange = br._G.GetSpellInfo(v)
+ local spellID = type(v) == "table" and (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(v) or v[1]) or v
+ local _, _, _, _, minRange = br.api.wow.GetSpellInfo(spellID)
return minRange or 0
end
@@ -57,7 +61,8 @@ br.api.spell = function(self, k, v)
-- @function spell.k.name
-- @return string
spell[k].name = function()
- local name = br._G.GetSpellInfo(v)
+ local spellID = type(v) == "table" and (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(v) or v[1]) or v
+ local name = br.api.wow.GetSpellInfo(spellID)
return name or ""
end
@@ -65,7 +70,8 @@ br.api.spell = function(self, k, v)
-- @function spell.k.rank
-- @return number
spell[k].rank = function()
- local _, rank = br._G.GetSpellInfo(v)
+ local spellID = type(v) == "table" and (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(v) or v[1]) or v
+ local _, rank = br.api.wow.GetSpellInfo(spellID)
return rank or 0
end
@@ -73,6 +79,7 @@ br.api.spell = function(self, k, v)
-- @function spell.k.texture
-- @return number
spell[k].texture = function()
- return br._G.C_Spell.GetSpellTexture(v)
+ local spellID = type(v) == "table" and (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(v) or v[1]) or v
+ return br._G.C_Spell.GetSpellTexture(spellID)
end
end
diff --git a/System/API/Talent.lua b/System/API/Talent.lua
index b40cd54e39..edadec0cfd 100644
--- a/System/API/Talent.lua
+++ b/System/API/Talent.lua
@@ -42,12 +42,16 @@ br.api.talent = function(talent, name, id, allTalents, spellList)
spellList[name] = id
end
end
- -- No matching talent for listed talent id, report to dev
+ -- No matching talent for listed talent id, report to dev (only for actual specs, not Initial profiles)
if (not talentFound or allTalents[id] == nil) then
- br._G.print("|cffff0000No talent found for: |r" ..
- name ..
- " (" ..
- id .. ") |cffff0000in the talent spell list, please notify profile developer to remove from the list.")
+
+ local spec = br._G.GetSpecializationInfo(br._G.GetSpecialization()) or 0
+ if spec < 1400 then
+ br._G.print("|cffff0000No talent found for: |r" ..
+ name ..
+ " (" ..
+ id .. ") |cffff0000in the talent spell list, please notify profile developer to remove from the list.")
+ end
return
end
------------------
diff --git a/System/API/Totem.lua b/System/API/Totem.lua
new file mode 100644
index 0000000000..3d1e44c932
--- /dev/null
+++ b/System/API/Totem.lua
@@ -0,0 +1,262 @@
+---
+-- Totem handling functions for BadRotations.
+-- Totem functions are stored in br.player.totem and can be utilized by `local totem = br.player.totem` in your profile.
+--
+-- This module provides a hierarchical API for totem checks:
+-- - totem.active(candidate) -> checks if any totem matching candidate is active
+-- - totem.fire.active() -> checks if any fire totem is active in slot 1
+-- - totem.fire.searingTotem.active() -> checks if searing totem specifically is active
+--
+-- Slot indices: 1 = Fire, 2 = Earth, 3 = Water, 4 = Air.
+-- @module br.player.totem
+local _, br = ...
+if br.api == nil then br.api = {} end
+
+local TOTEM_SLOT = {
+ fire = 1,
+ earth = 2,
+ water = 3,
+ air = 4,
+}
+
+br.api.totem = function(self)
+ self.totem = self.totem or {}
+ local totem = self.totem
+
+ local totemFn = br.functions and br.functions.totem
+
+ -- Export slot constants for rotations that want numeric slots.
+ totem.slot = TOTEM_SLOT
+
+ ---
+ -- Build a specific totem subtable (e.g., totem.fire.searingTotem)
+ -- @local
+ local function buildSpecificTotem(element, slot, totemKey)
+ local specific = {}
+
+ --- Check if this specific totem is active.
+ -- @function totem.element.totemName.active
+ -- @treturn boolean
+ specific.active = function()
+ if not totemFn then return false end
+ return totemFn:slotMatchesAny(slot, totemKey)
+ end
+
+ --- Alias for active.
+ -- @function totem.element.totemName.exists
+ -- @treturn boolean
+ specific.exists = specific.active
+
+ --- Get remaining time for this specific totem.
+ -- @function totem.element.totemName.remain
+ -- @treturn number
+ specific.remain = function()
+ if not specific.active() then return 0 end
+ if not totemFn then return 0 end
+ return totemFn:slotRemains(slot)
+ end
+
+ --- Get duration for this specific totem.
+ -- @function totem.element.totemName.duration
+ -- @treturn number
+ specific.duration = function()
+ if not specific.active() then return 0 end
+ if not totemFn then return 0 end
+ local haveTotem, _, _, duration = totemFn:getSlotInfo(slot)
+ return haveTotem and duration or 0
+ end
+
+ --- Get distance to this specific totem.
+ -- @function totem.element.totemName.distance
+ -- @treturn number
+ specific.distance = function()
+ if not specific.active() then return 999 end
+ if not totemFn or not totemFn.slotDistance then return 999 end
+ return totemFn:slotDistance(slot)
+ end
+
+ --- Get the name of this totem if active.
+ -- @function totem.element.totemName.name
+ -- @treturn string|nil
+ specific.name = function()
+ if not specific.active() then return nil end
+ if not totemFn then return nil end
+ local haveTotem, name = totemFn:getSlotInfo(slot)
+ return haveTotem and name or nil
+ end
+
+ return specific
+ end
+
+ ---
+ -- Build an element subtable (e.g., totem.fire)
+ -- @local
+ local function buildElementSubtable(elementKey, slot)
+ local element = {}
+ element.slot = slot
+
+ --- Check if any totem in this element slot is active.
+ -- @function totem.element.active
+ -- @treturn boolean
+ element.active = function()
+ if not totemFn then return false end
+ local haveTotem = totemFn:getSlotInfo(slot)
+ return haveTotem == true
+ end
+
+ --- Alias for active.
+ -- @function totem.element.exists
+ -- @treturn boolean
+ element.exists = element.active
+
+ --- Get the name of the active totem in this slot.
+ -- @function totem.element.name
+ -- @treturn string|nil
+ element.name = function()
+ if not totemFn then return nil end
+ local haveTotem, name = totemFn:getSlotInfo(slot)
+ return haveTotem and name or nil
+ end
+
+ --- Get remaining time for the active totem in this slot.
+ -- @function totem.element.remain
+ -- @treturn number
+ element.remain = function()
+ if not totemFn then return 0 end
+ return totemFn:slotRemains(slot)
+ end
+
+ --- Get duration for the active totem in this slot.
+ -- @function totem.element.duration
+ -- @treturn number
+ element.duration = function()
+ if not totemFn then return 0 end
+ local haveTotem, _, _, duration = totemFn:getSlotInfo(slot)
+ return haveTotem and duration or 0
+ end
+
+ --- Get distance to the active totem in this slot.
+ -- @function totem.element.distance
+ -- @treturn number
+ element.distance = function()
+ if not totemFn or not totemFn.slotDistance then return 999 end
+ return totemFn:slotDistance(slot)
+ end
+
+ --- Check if the active totem matches any of the provided candidates.
+ -- @function totem.element.is
+ -- @tparam ... candidates
+ -- @treturn boolean
+ element.is = function(...)
+ if not totemFn then return false end
+ return totemFn:slotMatchesAny(slot, ...)
+ end
+
+ -- Build specific totem subtables for this element from spell list
+ if self.spells and self.spells.totem and self.spells.totem[elementKey] then
+ for totemKey, _ in pairs(self.spells.totem[elementKey]) do
+ -- Convert totemKey to camelCase if needed (e.g., searingTotem -> searing)
+ -- Extract the totem name without "Totem" suffix for cleaner API
+ local shortName = totemKey:gsub("Totem$", "")
+ shortName = shortName:sub(1,1):lower() .. shortName:sub(2)
+ element[shortName] = buildSpecificTotem(elementKey, slot, totemKey)
+ end
+ end
+
+ return element
+ end
+
+ -- ========================================
+ -- Generic Totem Functions (any slot)
+ -- ========================================
+
+ ---
+ -- Returns raw slot info as reported by GetTotemInfo.
+ -- @function totem.get
+ -- @tparam number slot Totem slot (1-4)
+ -- @treturn boolean haveTotem
+ -- @treturn string|nil name
+ -- @treturn number startTime
+ -- @treturn number duration
+ -- @treturn number|nil icon
+ totem.get = function(slot)
+ if not totemFn then return false, nil, 0, 0, nil end
+ return totemFn:getSlotInfo(slot)
+ end
+
+ ---
+ -- Returns remaining time for a slot (seconds), or 0.
+ -- @function totem.remain
+ -- @tparam number slot Totem slot (1-4)
+ -- @treturn number
+ totem.remain = function(slot)
+ if not totemFn then return 0 end
+ return totemFn:slotRemains(slot)
+ end
+
+ ---
+ -- Returns distance from player to the active totem in a slot.
+ -- Returns 999 if no totem is active or the object can't be located.
+ -- @function totem.distance
+ -- @tparam number|string slotOrCandidate Totem slot (1-4) or totem spell key/id/name
+ -- @treturn number
+ totem.distance = function(slotOrCandidate)
+ if not totemFn then return 999 end
+
+ -- If it's a number, treat as slot
+ if type(slotOrCandidate) == "number" then
+ if not totemFn.slotDistance then return 999 end
+ return totemFn:slotDistance(slotOrCandidate)
+ end
+
+ -- Otherwise, treat as totem candidate
+ if not totemFn.distanceTo then return 999 end
+ return totemFn:distanceTo(slotOrCandidate)
+ end
+
+ ---
+ -- Returns true if the specified slot currently matches any provided totem.
+ -- Candidates can be spell IDs (number), spell keys from br.player.spells (string), or literal localized names (string).
+ -- @function totem.is
+ -- @tparam number slot Totem slot (1-4)
+ -- @tparam ... candidates
+ -- @treturn boolean
+ totem.is = function(slot, ...)
+ if not totemFn then return false end
+ return totemFn:slotMatchesAny(slot, ...)
+ end
+
+ ---
+ -- Returns true if the specified totem is currently active in any slot.
+ -- Candidate can be a spell key, spell ID, or localized name.
+ -- @function totem.active
+ -- @tparam string|number candidate
+ -- @treturn boolean
+ totem.active = function(candidate)
+ if not candidate then
+ -- No candidate provided, check if ANY totem is active
+ return totem.fire.active() or totem.earth.active() or totem.water.active() or totem.air.active()
+ end
+
+ if not totemFn then return false end
+ return totemFn:anySlotMatchesAny(candidate)
+ end
+
+ ---
+ -- Alias for active.
+ -- @function totem.exists
+ -- @tparam string|number candidate
+ -- @treturn boolean
+ totem.exists = function(candidate)
+ return totem.active(candidate)
+ end
+
+ -- ========================================
+ -- Element-specific Totem Tables
+ -- ========================================
+
+ totem.fire = buildElementSubtable("fire", TOTEM_SLOT.fire)
+ totem.earth = buildElementSubtable("earth", TOTEM_SLOT.earth)
+ totem.water = buildElementSubtable("water", TOTEM_SLOT.water)
+ totem.air = buildElementSubtable("air", TOTEM_SLOT.air)
+end
diff --git a/System/API/UI.lua b/System/API/UI.lua
index a86885d7fd..f4efd81867 100644
--- a/System/API/UI.lua
+++ b/System/API/UI.lua
@@ -16,32 +16,66 @@ br.api.ui = function(self)
ui.alwaysCdNever = function(thisOption)
-- Option Dropdown Requires
-- {"|cff008000Always", "|cff0000ffCD", "|cffff0000Never"}
- if br.data.settings[br.selectedSpec][br.selectedProfile]["Rotation Options"] == nil then return 0 end
- thisOption = br.data.settings[br.selectedSpec][br.selectedProfile]["Rotation Options"][thisOption] ~= nil and
- ui.value(thisOption, "Rotation Options") or
- ui.value(thisOption, "Base Options")
- return thisOption == 1 or (thisOption == 2 and ui.useCDs())
+ local settings = br.data.settings[br.loader.selectedSpec] and br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]
+ if not settings or settings["Rotation Options"] == nil then return false end
+
+ local optVal = settings["Rotation Options"][thisOption] ~= nil and ui.value(thisOption, "Rotation Options") or ui.value(thisOption, "Base Options")
+ return optVal == 1 or (optVal == 2 and ui.useCDs())
end
--- Checks if the passed option is set to "Always, AOE/CD, CD, or Never"
-- @function ui.alwaysCdAoENever
-- @string thisOption - Name of the option from the defined profile options.
- -- @number[opt=3] minUnits - Minimum Number of units to count for AOE checks.
- -- @number[opt] enemyCount - Number of enemies for a given range, default is the number of enemies in 40yrds.
+ -- @number[opt=3] minUnits - Minimum number of units required to treat this as AOE.
+ -- @number[opt] enemyCount - A precomputed enemy count (e.g. `#enemies.yards8`) provided by the caller.
-- @return boolean - Returns true based on selected option and current combat conditions
+ -- @example
+ -- -- Preferred (profile-controlled): compute a count and pass it
+ -- local use = ui.alwaysCdAoENever("Bladestorm", 3, #enemies.yards8)
+ --
+ -- -- Convenience wrapper (helper computes the count for you):
+ -- local use2 = ui.alwaysCdAoENeverRange("Bladestorm", 3, 8, "player")
+ -- -- Both forms return true if the Bladestorm option is set to Always, or set to AOE and there are at least 3 enemies,
+ -- -- or set to AOE/CD and either CDs are enabled or the count meets the threshold.
ui.alwaysCdAoENever = function(thisOption, minUnits, enemyCount)
-- Option Dropdown Requires
-- {"Always", "|cff008000AOE", "|cffffff00AOE/CD", "|cff0000ffCD", "|cffff0000Never"}
- if br.data.settings[br.selectedSpec][br.selectedProfile]["Rotation Options"] == nil then return 0 end
- thisOption = br.data.settings[br.selectedSpec][br.selectedProfile]["Rotation Options"][thisOption] ~= nil and
- ui.value(thisOption, "Rotation Options") or
- ui.value(thisOption, "Base Options")
+ local settings = br.data.settings[br.loader.selectedSpec] and br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]
+ if not settings or settings["Rotation Options"] == nil then return false end
+
+ local optVal = settings["Rotation Options"][thisOption] ~= nil and ui.value(thisOption, "Rotation Options") or ui.value(thisOption, "Base Options")
minUnits = minUnits or 3
- enemyCount = enemyCount or #br.getEnemies("player", 40, false, true)
- return thisOption == 1
- or (thisOption == 2 and enemyCount >= minUnits)
- or (thisOption == 3 and (ui.useCDs() or enemyCount >= minUnits))
- or (thisOption == 4 and ui.useCDs())
+
+ -- If explicitly Always/ Never/ CD-only selected, handle those first.
+ if optVal == 1 then return true end
+ if optVal == 5 then return false end
+ if optVal == 4 then return ui.useCDs() end
+
+ -- For AOE/CD or CD cases where AOE matters, require either CDs enabled or the provided enemyCount meets the threshold.
+ if optVal == 2 then
+ -- AOE selected: require enemyCount
+ return (enemyCount ~= nil and enemyCount >= minUnits)
+ end
+ if optVal == 3 then
+ -- AOE/CD selected: true if CDs enabled, otherwise require enemyCount
+ return ui.useCDs() or (enemyCount ~= nil and enemyCount >= minUnits)
+ end
+ return false
+ end
+
+ -- Convenience wrapper: compute enemy count from a range and delegate to ui.alwaysCdAoENever
+ -- @function ui.alwaysCdAoENeverRange
+ -- @string thisOption - Name of the option from the defined profile options.
+ -- @number[opt=3] minUnits - Minimum number of units required to treat this as AOE.
+ -- @number[opt=8] range - Range in yards to check for enemies.
+ -- @string[opt="player"] useTarget - Reference unit used to evaluate range (defaults to `player`).
+ -- @return boolean
+ ui.alwaysCdAoENeverRange = function(thisOption, minUnits, range, useTarget)
+ if minUnits == nil then minUnits = 3 end
+ if range == nil then range = 8 end
+ if useTarget == nil then useTarget = "player" end
+ local _, enemyCount = self.enemies.get(range, useTarget)
+ return ui.alwaysCdAoENever(thisOption, minUnits, enemyCount)
end
if ui.chatOverlay == nil then
@@ -50,7 +84,7 @@ br.api.ui = function(self)
-- @string text - The text to display.
-- @return nil
ui.chatOverlay = function(text)
- return br.ChatOverlay(text)
+ return br.ui.chatOverlay:Show(text)
end
end
@@ -62,7 +96,7 @@ br.api.ui = function(self)
-- @return boolean - Returns true if the option is checked
ui.checked = function(thisOption, optionPage)
if thisOption == nil then return false end
- return br.isChecked(thisOption, optionPage)
+ return br.functions.misc:isChecked(thisOption, optionPage)
end
end
@@ -72,7 +106,7 @@ br.api.ui = function(self)
-- @string text - The message to show in chat.
-- @return nil
ui.debug = function(text)
- return br.addonDebug(text)
+ return br.functions.misc:addonDebug(text)
end
end
@@ -83,7 +117,7 @@ br.api.ui = function(self)
-- @number delayTime - The length of time to wait until true, in seconds.
-- @return boolean - Returns true if the delay time has passed
ui.delay = function(delayName, delayTime)
- return br.timer:useTimer(delayName, delayTime)
+ return br.debug.timer:useTimer(delayName, delayTime)
end
end
@@ -92,7 +126,27 @@ br.api.ui = function(self)
-- @function ui.fullBags
-- @return boolean - Returns true if bags are full
ui.fullBags = function()
- return br.lootManager:emptySlots() == 0
+ return br.engines.lootEngine:emptySlots() == 0
+ end
+ end
+
+ if ui.getSpell == nil then
+ --- Returns the spellID for the highest known if table is passed, or the spellID for the single spell name.
+ -- @function ui.getSpell
+ -- @string spellName - The name of the spell.
+ -- @return number - Returns the spell ID
+ ui.getSpell = function(spellID)
+ if type(spellID) == "table" then
+ -- return highest rank spell that is known
+ for i = #spellID, 1, -1 do
+ if br.functions.spell:isKnown(spellID[i]) then
+ return spellID[i]
+ end
+ end
+ return nil
+ else
+ return spellID
+ end
end
end
@@ -114,14 +168,14 @@ br.api.ui = function(self)
if ui.pause == nil then
--- Returns true if special conditions are met to pause
- -- @see br.pause for these conditions
+ -- @see br.functions.misc:pause for these conditions
-- @function ui.pause
-- @boolean[opt=false] ignoreChannel - Set to true to ignore pausing on channel casts
-- @return boolean - Returns true if rotation should be paused
ui.pause = function(ignoreChannel)
--local pause = br._G["pause"]
if ignoreChannel == nil then ignoreChannel = false end
- return br.pause(ignoreChannel)
+ return br.functions.misc:pause(ignoreChannel)
end
end
@@ -147,8 +201,19 @@ br.api.ui = function(self)
-- @function ui.pullTimer
-- @return number - Returns seconds remaining on pull timer
ui.pullTimer = function()
- --local PullTimerRemain = br._G["PullTimerRemain"]
- return br.PullTimerRemain()
+ return br.functions.custom:PullTimerRemain()
+ end
+ end
+
+ if ui.round == nil then
+ --- Rounds a number to the specified number of decimal places (default 2)
+ -- @function ui.round
+ -- @number num - The number to round.
+ -- @number[opt=2] numDecimalPlaces - The number of decimal places to round to.
+ -- @return number - Returns the rounded number
+ ui.round = function(num, numDecimalPlaces)
+ if numDecimalPlaces == nil then numDecimalPlaces = 2 end
+ return br.functions.misc:round2(num, numDecimalPlaces)
end
end
@@ -168,7 +233,7 @@ br.api.ui = function(self)
-- @number interval - The length of time to wait until true, in seconds.
-- @return boolean - Returns true if the interval has passed
ui.timer = function(timerName, interval)
- return br.timer:useTimer(timerName, interval)
+ return br.debug.timer:useTimer(timerName, interval)
end
end
@@ -178,7 +243,7 @@ br.api.ui = function(self)
-- @string thisToggle - Name of the toggle to check.
-- @return boolean - Returns true if the toggle is active
ui.toggle = function(thisToggle)
- return not br._G.GetCurrentKeyBoardFocus() and br.SpecificToggle(thisToggle) or false
+ return not br._G.GetCurrentKeyBoardFocus() and br.functions.misc:SpecificToggle(thisToggle) or false
end
end
@@ -204,7 +269,7 @@ br.api.ui = function(self)
-- @return boolean - Returns true if cooldowns should be used
ui.useCDs = function()
local hasBloodLust = br._G["hasBloodLust"]
- return (ui.mode.cooldown == 1 and br.isBoss())
+ return (ui.mode.cooldown == 1 and br.functions.unit:isBoss())
or ui.mode.cooldown == 2
or (ui.mode.cooldown == 4 and hasBloodLust())
end
@@ -271,7 +336,19 @@ br.api.ui = function(self)
-- @return number - Returns the value of the specified option
ui.value = function(thisOption, optionPage)
if thisOption == nil then return 0 end
- return br.getOptionValue(thisOption, optionPage)
+ return br.functions.misc:getOptionValue(thisOption, optionPage)
+ end
+ end
+
+ if ui.setToggle == nil then
+ --- Sets a toggle to the specified value and updates the UI
+ -- @function ui.setToggle
+ -- @string toggleName - The name of the toggle to set
+ -- @number toggleValue - The value to set the toggle to (1, 2, 3, etc.)
+ -- @return nil
+ ui.setToggle = function(toggleName, toggleValue)
+ if toggleName == nil or toggleValue == nil then return end
+ br.ui:ToggleToValue(toggleName, toggleValue)
end
end
end
diff --git a/System/API/Unit.lua b/System/API/Unit.lua
index 938f9206b2..1a8f35d7a0 100644
--- a/System/API/Unit.lua
+++ b/System/API/Unit.lua
@@ -3,6 +3,7 @@
-- Provides unit-based check functionality for the rotation system.
-- These calls help in retrieving information about unit-based checks.
-- @usage local unit = br.player.unit
+
local _, br = ...
if br.api == nil then br.api = {} end
----------------------
@@ -16,6 +17,36 @@ br.api.unit = function(self)
-- Local reference to unit
local unit = self.unit
+ local function getSpellId(spellRef)
+ if spellRef == nil then return nil end
+ local refType = type(spellRef)
+ if refType == "number" or refType == "string" then
+ return spellRef
+ end
+ if refType == "table" then
+ if type(spellRef.id) == "function" then
+ local ok, value = pcall(spellRef.id)
+ if ok and value ~= nil then
+ return value
+ end
+ elseif type(spellRef.id) == "number" then
+ return spellRef.id
+ end
+ end
+ return nil
+ end
+
+ local function getManaCost(spellRef, treatAsFree)
+ if treatAsFree then return 0 end
+ local id = getSpellId(spellRef)
+ if id == nil then return 0 end
+ local cost, _, costType = br.functions.power:getSpellCost(id)
+ if not cost or cost <= 0 then return 0 end
+ -- Mana is power type 0.
+ if costType ~= 0 then return 0 end
+ return cost
+ end
+
----------------
--- Unit API ---
----------------
@@ -25,9 +56,8 @@ br.api.unit = function(self)
-- @param thisUnit The unit to check, defaults to "target" if nil
-- @return boolean True if the unit is an Aberration
unit.aberration = function(thisUnit)
- local isAberration = br["isAberration"]
if thisUnit == nil then thisUnit = "target" end
- return isAberration(thisUnit)
+ return br.functions.unit:isAberration(thisUnit)
end
--- Check if a unit is a Beast
@@ -35,9 +65,8 @@ br.api.unit = function(self)
-- @param thisUnit The unit to check, defaults to "target" if nil
-- @return boolean True if the unit is a Beast
unit.beast = function(thisUnit)
- local isBeast = br["isBeast"]
if thisUnit == nil then thisUnit = "target" end
- return isBeast(thisUnit)
+ return br.functions.unit:isBeast(thisUnit)
end
--- Check if a unit can be attacked
@@ -51,6 +80,13 @@ br.api.unit = function(self)
return UnitCanAttack(thisUnit, playerUnit)
end
+ --- Check if player can fly
+ -- @function unit.canFly
+ -- @return boolean True if the player can fly in the current area
+ unit.canFly = function()
+ return br.functions.action:canFly()
+ end
+
--- Cancel current shapeshift form
-- @function unit.cancelForm
-- @return nil
@@ -59,6 +95,47 @@ br.api.unit = function(self)
return CancelShapeshiftForm() --or RunMacroText("/CancelForm")
end
+ --- Calculate mana required to safely break form for a spell and shift back.
+ -- @function unit.formBreakManaRequired
+ -- @param outOfFormSpell Spell reference (id/name/spell table) you intend to cast after breaking form.
+ -- @param returnFormSpell Spell reference (id/name/spell table) to shift back into.
+ -- @param treatOutSpellAsFree If true, treat out-of-form spell cost as 0 (e.g., Clearcasting).
+ -- @treturn number requiredMana Total mana needed (out spell + return form).
+ -- @treturn number currentMana Current player mana.
+ unit.formBreakManaRequired = function(outOfFormSpell, returnFormSpell, treatOutSpellAsFree)
+ local outCost = getManaCost(outOfFormSpell, treatOutSpellAsFree)
+ local backCost = getManaCost(returnFormSpell, false)
+ local requiredMana = outCost + backCost
+ local currentMana = br._G.UnitPower("player", 0)
+ return requiredMana, currentMana
+ end
+
+ --- Check if it's safe to cancel form (enough mana to cast the spell and shift back).
+ -- @function unit.canCancelFormFor
+ -- @param outOfFormSpell Spell reference (id/name/spell table) you intend to cast after breaking form.
+ -- @param returnFormSpell Spell reference (id/name/spell table) to shift back into.
+ -- @param treatOutSpellAsFree If true, treat out-of-form spell cost as 0 (e.g., Clearcasting).
+ -- @treturn boolean
+ unit.canCancelFormFor = function(outOfFormSpell, returnFormSpell, treatOutSpellAsFree)
+ local requiredMana, currentMana = unit.formBreakManaRequired(outOfFormSpell, returnFormSpell, treatOutSpellAsFree)
+ if requiredMana <= 0 then return true end
+ return currentMana >= requiredMana
+ end
+
+ --- Cancel form only if safe to do so.
+ -- @function unit.cancelFormFor
+ -- @param outOfFormSpell Spell reference (id/name/spell table) you intend to cast after breaking form.
+ -- @param returnFormSpell Spell reference (id/name/spell table) to shift back into.
+ -- @param treatOutSpellAsFree If true, treat out-of-form spell cost as 0 (e.g., Clearcasting).
+ -- @treturn boolean True if the form was cancelled.
+ unit.cancelFormFor = function(outOfFormSpell, returnFormSpell, treatOutSpellAsFree)
+ if not unit.canCancelFormFor(outOfFormSpell, returnFormSpell, treatOutSpellAsFree) then
+ return false
+ end
+ unit.cancelForm()
+ return true
+ end
+
--- Check if a unit is casting or channeling a spell
-- @function unit.casting
-- @param thisUnit The unit to check, defaults to "player" if nil
@@ -71,7 +148,7 @@ br.api.unit = function(self)
spellCasting = br._G.UnitChannelInfo(thisUnit)
end
if spellID == nil then return spellCasting ~= nil end
- local spellName = br._G.GetSpellInfo(spellID)
+ local spellName = br.api.wow.GetSpellInfo(spellID)
return tostring(spellCasting) == tostring(spellName)
end
@@ -79,15 +156,14 @@ br.api.unit = function(self)
-- @function unit.combatTime
-- @return number Time in combat in seconds
unit.combatTime = function()
- return br.getCombatTime()
+ return br.functions.combat:getCombatTime()
end
--- Get time out of combat
-- @function unit.ooCombatTime
-- @return number Time out of combat in seconds
unit.ooCombatTime = function()
- local getOoCTime = br["getOoCTime()"]
- return getOoCTime()
+ return br.functions.combat:getOoCTime()
end
--- Check if a unit is charmed
@@ -95,8 +171,7 @@ br.api.unit = function(self)
-- @param thisUnit The unit to check
-- @return boolean True if the unit is charmed
unit.charmed = function(thisUnit)
- local UnitIsCharmed = br._G["UnitIsCharmed"]
- return UnitIsCharmed(thisUnit)
+ return br.functions.unit:UnitIsCharmed(thisUnit)
end
--- Clear current target
@@ -120,9 +195,8 @@ br.api.unit = function(self)
-- @param thisUnit The unit to check, defaults to "target" if nil
-- @return boolean True if the unit is a Demon
unit.demon = function(thisUnit)
- local isDemon = br["isDemon"]
if thisUnit == nil then thisUnit = "target" end
- return isDemon(thisUnit)
+ return br.functions.unit:isDemon(thisUnit)
end
--- Get distance between units
@@ -135,7 +209,7 @@ br.api.unit = function(self)
otherUnit = thisUnit
thisUnit = "player"
end
- return br.getDistance(thisUnit, otherUnit)
+ return br.functions.range:getDistance(thisUnit, otherUnit)
end
--- Check if player is dual wielding weapons
@@ -162,8 +236,7 @@ br.api.unit = function(self)
-- @param thisUnit The unit to check
-- @return boolean True if the unit exists
unit.exists = function(thisUnit)
- local UnitExists = br["GetUnitExists"]
- return UnitExists(thisUnit)
+ return br.functions.unit:GetUnitExists(thisUnit)
end
--- Check if a unit is facing another unit
@@ -176,7 +249,27 @@ br.api.unit = function(self)
if otherUnit == nil then
otherUnit = thisUnit; thisUnit = "player"
end
- return br.getFacing(thisUnit, otherUnit, degrees)
+ return br.functions.unit:getFacing(thisUnit, otherUnit, degrees)
+ end
+
+ --- Check if a unit is behind another unit
+ -- @function unit.isBehind
+ -- @param thisUnit The unit being checked for facing (typically the target), defaults to "target" if nil
+ -- @param otherUnit The reference unit (typically "player"), defaults to "player" if nil
+ -- @param degrees Optional angle in degrees for the facing cone, defaults to normal game value
+ -- @return boolean True if thisUnit is NOT facing otherUnit within the given degrees
+ unit.isBehind = function(thisUnit, otherUnit, degrees)
+ if thisUnit == nil then thisUnit = "target" end
+ if otherUnit == nil then otherUnit = "player" end
+
+ local facing = br.functions.unit:getFacing(thisUnit, otherUnit, degrees)
+ if facing == nil then
+ if br and br.ui and br.ui.debug then
+ br.ui.debug("isBehind: facing=nil | thisUnit="..tostring(thisUnit).." | otherUnit="..tostring(otherUnit).." | degrees="..tostring(degrees))
+ end
+ return false
+ end
+ return not facing
end
--- Check if a unit is falling
@@ -191,8 +284,14 @@ br.api.unit = function(self)
-- @function unit.fallTime
-- @return number Time in seconds player has been falling
unit.fallTime = function()
- local getFallTime = br["getFallTime"]
- return getFallTime()
+ return br.functions.misc:getFallTime()
+ end
+
+ --- Get the distance the player has fallen
+ --- @function unit.fallDist
+ --- @return number Distance in yards the player has fallen
+ unit.fallDist = function()
+ return br.functions.misc:getFallDistance() or 0
end
--- Check if player is flying
@@ -219,13 +318,52 @@ br.api.unit = function(self)
return GetNumShapeshiftForms()
end
+ --- Get information about a specific shapeshift form
+ -- @function unit.formInfo
+ -- @param formIndex The index of the form (1-based)
+ -- @return texture, isActive, isCastable, spellID
+ unit.formInfo = function(formIndex)
+ local GetShapeshiftFormInfo = br._G["GetShapeshiftFormInfo"]
+ if GetShapeshiftFormInfo then
+ return GetShapeshiftFormInfo(formIndex)
+ end
+ return nil, false, false, nil
+ end
+
+ --- Check if a specific form is active or if any form is active
+ -- @function unit.formActive
+ -- @param spellID Optional spell ID to check for a specific form. If nil, checks if any form is active
+ -- @return boolean True if the specified form (or any form if no spellID provided) is active
+ unit.formActive = function(spellID)
+ local numForms = unit.formCount()
+ if spellID then
+ -- Check for specific form by spell ID
+ for i = 1, numForms do
+ local _, isActive, _, formSpellID = unit.formInfo(i)
+ if isActive and formSpellID == spellID then
+ return true
+ end
+ end
+ return false
+ else
+ -- Check if any form is active
+ for i = 1, numForms do
+ local _, isActive = unit.formInfo(i)
+ if isActive then
+ return true
+ end
+ end
+ return false
+ end
+ end
+
--- Check if a unit is friendly to another unit
-- @function unit.friend
-- @param thisUnit The unit to check
-- @param playerUnit The reference unit, defaults to "player" if nil
-- @return boolean True if thisUnit is friendly to playerUnit
unit.friend = function(thisUnit, playerUnit)
- local UnitIsFriend = br["GetUnitIsFriend"]
+ local UnitIsFriend = br._G["UnitIsFriend"]
if playerUnit == nil then playerUnit = "player" end
return UnitIsFriend(thisUnit, playerUnit)
end
@@ -235,7 +373,7 @@ br.api.unit = function(self)
-- @param max Optional boolean to return maximum possible GCD instead of current
-- @return number Global cooldown in seconds
unit.gcd = function(max)
- return br.getGlobalCD(max)
+ return br.functions.spell:getGlobalCD(max)
end
--- Get unit's GUID (Globally Unique Identifier)
@@ -253,6 +391,9 @@ br.api.unit = function(self)
-- @return number Current health of the unit
unit.health = function(thisUnit)
if thisUnit == nil then thisUnit = "player" end
+ if unit.isDummy(thisUnit) then
+ return br._G.UnitHealthMax(thisUnit)
+ end
return br._G.UnitHealth(thisUnit)
end
@@ -271,9 +412,11 @@ br.api.unit = function(self)
-- @param thisUnit The unit to check, defaults to "player" if nil
-- @return number Health percentage of the unit (0-100)
unit.hp = function(thisUnit)
- local getHP = br["getHP"]
if thisUnit == nil then thisUnit = "player" end
- return br.round2(getHP(thisUnit), 2)
+ if unit.isDummy(thisUnit) then
+ return 100
+ end
+ return br.functions.misc:round2(br.functions.unit:getHP(thisUnit), 2)
end
--- Check if a unit is a Humanoid
@@ -281,9 +424,8 @@ br.api.unit = function(self)
-- @param thisUnit The unit to check, defaults to "target" if nil
-- @return boolean True if the unit is a Humanoid
unit.humanoid = function(thisUnit)
- local isHumanoid = br["isHumanoid"]
if thisUnit == nil then thisUnit = "target" end
- return isHumanoid(thisUnit)
+ return br.functions.unit:isHumanoid(thisUnit)
end
--- Get unit's ID
@@ -291,7 +433,7 @@ br.api.unit = function(self)
-- @param thisUnit The unit to get ID for
-- @return number Unit's ID
unit.id = function(thisUnit)
- return br.GetObjectID(thisUnit)
+ return br.functions.unit:GetObjectID(thisUnit)
end
--- Check if a unit is in combat
@@ -326,7 +468,7 @@ br.api.unit = function(self)
unit.interruptable = function(thisUnit, castPercent)
if thisUnit == nil then thisUnit = "target" end
if castPercent == nil then castPercent = 0 end
- return br.canInterrupt(thisUnit, castPercent)
+ return br.functions.spell:canInterrupt(thisUnit, castPercent)
end
--- Check if a unit is a boss
@@ -335,7 +477,7 @@ br.api.unit = function(self)
-- @return boolean True if the unit is a boss
unit.isBoss = function(thisUnit)
if thisUnit == nil then thisUnit = "target" end
- return br.isBoss(thisUnit)
+ return br.functions.unit:isBoss(thisUnit)
end
--- Check if a unit is casting
@@ -353,7 +495,16 @@ br.api.unit = function(self)
-- @param thisUnit The unit to check
-- @return boolean True if the unit is a training dummy
unit.isDummy = function(thisUnit)
- return br.isDummy(thisUnit)
+ return br.functions.unit:isDummy(thisUnit)
+ end
+
+ --- Check if a unit is an Elemental
+ -- @function unit.isElemental
+ -- @param thisUnit The unit to check, defaults to "target" if nil
+ -- @return boolean True if the unit is an Elemental
+ unit.isElemental = function(thisUnit)
+ if thisUnit == nil then thisUnit = "target" end
+ return br.functions.unit:isElemental(thisUnit)
end
--- Check if a unit is an explosive orb (M+ affix)
@@ -361,8 +512,15 @@ br.api.unit = function(self)
-- @param thisUnit The unit to check
-- @return boolean True if the unit is an explosive orb
unit.isExplosive = function(thisUnit)
- local isExplosive = br["isExplosive"]
- return isExplosive(thisUnit)
+ return br.functions.unit:isExplosive(thisUnit)
+ end
+
+ --- Check if a unit is fleeing
+ -- @function unit.isFleeing
+ -- @param thisUnit The unit to check
+ -- @return boolean True if the unit is fleeing
+ unit.isFleeing = function(thisUnit)
+ return br.functions.unit:isFleeing(thisUnit)
end
--- Check if a unit is tanking
@@ -371,14 +529,14 @@ br.api.unit = function(self)
-- @return boolean True if the unit is tanking
unit.isTanking = function(thisUnit)
if thisUnit == nil then thisUnit = "player" end
- return br.isTanking(thisUnit)
+ return br.functions.combat:isTanking(thisUnit)
end
--- Check if a tank is in range
-- @function unit.isTankInRange
-- @return boolean True if a tank is in range
unit.isTankInRange = function()
- return br.isTankInRange()
+ return br.functions.unit:isTankInRange()
end
--- Check if two units are the same
@@ -407,9 +565,8 @@ br.api.unit = function(self)
-- @param range The range to check for units, defaults to 5 if nil
-- @return unit The friendly unit with lowest health in range
unit.lowest = function(range)
- local getLowestUnit = br["getLowestUnit"]
if range == nil then range = 5 end
- return getLowestUnit(range)
+ return br.functions.unit:getLowestUnit(range)
end
--- Check if player is mounted
@@ -433,6 +590,7 @@ br.api.unit = function(self)
--- Get time a unit has been moving
-- @function unit.movingTime
-- @return number Time in seconds the unit has been moving
+ local movingTimer
unit.movingTime = function()
local GetTime = br._G["GetTime"]
if movingTimer == nil then movingTimer = GetTime() end
@@ -448,6 +606,7 @@ br.api.unit = function(self)
-- @return string The name of the unit
unit.name = function(thisUnit)
local UnitName = br._G["UnitName"]
+ if thisUnit == nil then return "" end
return UnitName(thisUnit)
end
@@ -484,9 +643,8 @@ br.api.unit = function(self)
-- @param playerUnit The reference unit, defaults to "player" if nil
-- @return number Reaction level (1-hostile to 8-friendly)
unit.reaction = function(thisUnit, playerUnit)
- local GetUnitReaction = br["GetUnitReaction"]
if playerUnit == nil then playerUnit = "player" end
- return GetUnitReaction(thisUnit, playerUnit)
+ return br.functions.unit:GetUnitReaction(thisUnit, playerUnit)
end
--- Check if player is resting
@@ -510,7 +668,14 @@ br.api.unit = function(self)
-- @function unit.solo
-- @return boolean True if player is solo
unit.solo = function()
- return #br.friend == 1
+ return #br.engines.healingEngine.friend == 1
+ end
+
+ --- Check if player is in a group (party or raid)
+ -- @function unit.inGroup
+ -- @return boolean True if player is in a group
+ unit.inGroup = function()
+ return #br.engines.healingEngine.friend > 1
end
--- Get player's spell haste percentage
@@ -571,7 +736,15 @@ br.api.unit = function(self)
-- @return boolean True if the unit has threat
unit.threat = function(thisUnit)
if thisUnit == nil then thisUnit = "target" end
- return br.hasThreat(thisUnit)
+ return br.functions.combat:hasThreat(thisUnit)
+ end
+
+ --- Get the player's current threat status
+ -- @function unit.threatStatus
+ -- @return number Threat status: 0=no/low threat, 1=high threat (not primary target),
+ -- 2=primary target (another unit has 100%+ threat), 3=primary target (sole aggro)
+ unit.threatStatus = function()
+ return br.functions.custom:getThreat()
end
--- Estimate time until unit dies
@@ -581,7 +754,7 @@ br.api.unit = function(self)
-- @return number Estimated time until death in seconds
unit.ttd = function(thisUnit, percent)
if thisUnit == nil then thisUnit = "target" end
- return br.getTTD(thisUnit, percent) or 0
+ return br.engines.ttdTable:getTTD(thisUnit, percent) or 0
end
--- Get combined time to death for all enemies in range
@@ -604,8 +777,7 @@ br.api.unit = function(self)
-- @param thisUnit The unit to check
-- @return boolean True if the unit is Undead
unit.undead = function(thisUnit)
- local isUndead = br["isUndead"]
- return isUndead(thisUnit)
+ return br.functions.unit:isUndead(thisUnit)
end
--- Check if a unit is valid for targeting
@@ -613,7 +785,7 @@ br.api.unit = function(self)
-- @param thisUnit The unit to check
-- @return boolean True if the unit is valid
unit.valid = function(thisUnit)
- return br.isValidUnit(thisUnit)
+ return br.functions.misc:isValidUnit(thisUnit)
end
-- Weapon Imbue Fuctions
@@ -669,4 +841,40 @@ br.api.unit = function(self)
if not offHand and unit.weaponImbue.exists(imbueId) then return mainCharges end
return 0
end
+
+ ----------------------
+ --- Combat Stats API ---
+ ----------------------
+
+ --- Get player's attack power
+ -- @function unit.attackPower
+ -- @param thisUnit The unit to check, defaults to "player" if nil
+ -- @return number Total attack power (base + positive buffs + negative buffs)
+ unit.attackPower = function(thisUnit)
+ if thisUnit == nil then thisUnit = "player" end
+ local UnitAttackPower = br._G["UnitAttackPower"]
+ local base, posBuff, negBuff = UnitAttackPower(thisUnit)
+ return base + posBuff + negBuff
+ end
+
+ --- Get player's critical strike chance
+ -- @function unit.critChance
+ -- @param thisUnit The unit to check, defaults to "player" if nil
+ -- @return number Critical strike chance percentage
+ unit.critChance = function(thisUnit)
+ if thisUnit == nil then thisUnit = "player" end
+ local GetCritChance = br._G["GetCritChance"]
+ return GetCritChance()
+ end
+
+ --- Get player's average weapon damage
+ -- @function unit.weaponDamage
+ -- @param thisUnit The unit to check, defaults to "player" if nil
+ -- @return number Average weapon damage
+ unit.weaponDamage = function(thisUnit)
+ if thisUnit == nil then thisUnit = "player" end
+ local UnitDamage = br._G["UnitDamage"]
+ local minDmg, maxDmg = UnitDamage(thisUnit)
+ return (minDmg + maxDmg) / 2
+ end
end
diff --git a/System/API/Units.lua b/System/API/Units.lua
index b0e7eb0362..e453c3cfe4 100644
--- a/System/API/Units.lua
+++ b/System/API/Units.lua
@@ -19,10 +19,28 @@ br.api.units = function(self)
aoe = aoe or false
if aoe then dynString = dynString.."AOE" end
local facing = not aoe
- local thisUnit = br.dynamicTarget(range, facing)
+ local thisUnit = br.engines.enemiesEngineFunctions:dynamicTarget(range, facing)
-- Build units.dyn varaible
units[dynString] = units[dynString] or {}
units[dynString] = thisUnit
return thisUnit -- Backwards compatability for old way
end
+
+ --- Custom target weight function that profiles can override to add coefficient bonuses for dynamic targeting.
+ -- This allows profiles to customize target prioritization beyond the default coefficient system.
+ -- Return a number to add to the unit's coefficient (higher = more priority).
+ -- @function units.customTargetWeight
+ -- @param unit - The unit to calculate custom weight for
+ -- @returns number - Additional coefficient to add (default 0)
+ -- @usage
+ -- -- Example: Feral druid prioritizing Rip refresh
+ -- units.customTargetWeight = function(thisUnit)
+ -- if debuff.rip.exists(thisUnit) and debuff.rip.remains(thisUnit) < 5 then
+ -- return 80 -- High priority to refresh expiring Rip
+ -- end
+ -- return 0
+ -- end
+ units.customTargetWeight = function(unit)
+ return 0 -- Default: no custom weight
+ end
end
diff --git a/System/API/Use.lua b/System/API/Use.lua
index 6e96719186..c5e0848d9f 100644
--- a/System/API/Use.lua
+++ b/System/API/Use.lua
@@ -19,9 +19,11 @@ br.api.use = function(self,item,id)
use[item] = function(slotID,thisUnit)
if thisUnit == nil then thisUnit = "target" end
if slotID == nil then
- if br.canUseItem(id) then return br.useItem(id,thisUnit) else return end
+ local resolvedID = type(id) == "table" and br.functions.item:getHighestHeldRank(id) or id
+ if resolvedID == nil then return false end
+ if br.functions.item:canUseItem(resolvedID) then return br.functions.item:useItem(resolvedID,thisUnit) else return end
else
- if br.canUseItem(slotID) then return br.useItem(slotID,thisUnit) else return end
+ if br.functions.item:canUseItem(slotID) then return br.functions.item:useItem(slotID,thisUnit) else return end
end
end
@@ -35,8 +37,8 @@ br.api.use = function(self,item,id)
use.item = function(itemID,thisUnit)
if itemID == nil then return false end
if thisUnit == nil then thisUnit = "target" end
- if br.canUseItem(itemID) then
- return br.useItem(itemID,thisUnit)
+ if br.functions.item:canUseItem(itemID) then
+ return br.functions.item:useItem(itemID,thisUnit)
else
return
end
@@ -56,8 +58,8 @@ br.api.use = function(self,item,id)
if itemIDs == nil then return false end
if thisUnit == nil then thisUnit = "player" end
for i=1,#itemIDs do
- if br.canUseItem(itemIDs[i]) then
- return br.useItem(itemIDs[i],thisUnit)
+ if br.functions.item:canUseItem(itemIDs[i]) then
+ return br.functions.item:useItem(itemIDs[i],thisUnit)
end
end
end
@@ -73,7 +75,7 @@ br.api.use = function(self,item,id)
use.isOneOfUsable = function(itemIDs)
if itemIDs == nil then return false end
for i=1, #itemIDs do
- if br.canUseItem(itemIDs[i]) then return true end
+ if br.functions.item:canUseItem(itemIDs[i]) then return true end
end
return false
end
@@ -87,7 +89,7 @@ br.api.use = function(self,item,id)
-- @treturn boolean
use.slot = function(slotID,thisUnit)
if thisUnit == nil then thisUnit = "target" end
- if br.canUseItem(slotID) then return br.useItem(slotID,thisUnit) else return end
+ if br.functions.item:canUseItem(slotID) then return br.functions.item:useItem(slotID,thisUnit) else return end
end
end
@@ -98,7 +100,10 @@ br.api.use = function(self,item,id)
-- @number[opt] slotID The ID of the equipment slot to check.
-- @treturn boolean
use.able[item] = function(slotID)
- if slotID == nil then return br.canUseItem(id) else return br.canUseItem(slotID) end
+ if slotID ~= nil then return br.functions.item:canUseItem(slotID) end
+ local resolvedID = type(id) == "table" and br.functions.item:getHighestHeldRank(id) or id
+ if resolvedID == nil then return false end
+ return br.functions.item:canUseItem(resolvedID)
end
if use.able.item == nil then
@@ -109,7 +114,7 @@ br.api.use = function(self,item,id)
-- @treturn boolean
use.able.item = function(itemID)
if itemID == nil then return false end
- return br.canUseItem(itemID)
+ return br.functions.item:canUseItem(itemID)
end
end
@@ -119,7 +124,7 @@ br.api.use = function(self,item,id)
-- @number slotID The ID of the equipment slot to check.
-- @treturn boolean
use.able.slot = function(slotID)
- return br.canUseItem(slotID)
+ return br.functions.item:canUseItem(slotID)
end
end
end
\ No newline at end of file
diff --git a/System/Core.lua b/System/Core.lua
index c347584a96..a95dfc68bb 100644
--- a/System/Core.lua
+++ b/System/Core.lua
@@ -1,107 +1,119 @@
local _, br = ...
-br.engines = {}
+br.engines = br.engines or {}
+local engines = br.engines
-function br:getUpdateRate()
- local updateRate = 0.1
+function engines:getUpdateRate()
+ local updateRate = br.functions.misc:getOptionValue("Bot Update Rate") or 0.1
- local FrameRate = br._G.GetFramerate() or 0
- if br.isChecked("Auto Delay") then
+ if br.functions.misc:isChecked("Auto Delay") then
+ local FrameRate = br._G.GetFramerate() or 0
if FrameRate >= 0 and FrameRate < 60 then
updateRate = (60 - FrameRate) / 60
- else
- updateRate = 0.1
end
- elseif br.getOptionValue("Bot Update Rate") == nil then
- updateRate = 0.1
- else
- updateRate = br.getOptionValue("Bot Update Rate")
end
return updateRate
end
-- Main Engine
-function br:Engine()
- if br.engines.Pulse_Engine == nil then
- br.engines.Pulse_Engine = br._G.CreateFrame("Frame", nil, br._G.UIParent)
- br.engines.Pulse_Engine:SetScript("OnUpdate", br.BadRotationsUpdate)
- br.engines.Pulse_Engine:Show()
+function engines:Main()
+ if engines.Pulse_Engine == nil then
+ engines.Pulse_Engine = br._G.CreateFrame("Frame", nil, br._G.UIParent)
+ engines.Pulse_Engine:SetScript("OnUpdate", engines.Update)
+ engines.Pulse_Engine:Show()
end
end
-- Object Manager Engine
-local function ObjectManagerUpdate(self)
+local function ObjectManagerUpdate(self, elapsed)
if br.unlocked then
- if br.data ~= nil and br.data.settings ~= nil and br.data.settings[br.selectedSpec] ~= nil
- and br.data.settings[br.selectedSpec].toggles ~= nil
+ if br.data ~= nil and br.data.settings ~= nil and br.data.settings[br.loader.selectedSpec] ~= nil
+ and br.data.settings[br.loader.selectedSpec].toggles ~= nil
then
- if br.data.settings[br.selectedSpec].toggles["Power"] ~= nil
- and br.data.settings[br.selectedSpec].toggles["Power"] == 1
+ if br.data.settings[br.loader.selectedSpec].toggles["Power"] ~= nil
+ and br.data.settings[br.loader.selectedSpec].toggles["Power"] == 1
then
+ self.omElapsed = (self.omElapsed or 0) + (elapsed or 0)
+ local omInterval = math.max(0.05, engines:getUpdateRate())
+ if self.omElapsed < omInterval then
+ return
+ end
+ self.omElapsed = 0
-- attempt to update objects every frame
-- updates for each object will be spread out randomly
- br:updateOM()
- br.om:Update()
+ engines.enemiesEngineFunctions:updateOM()
+ engines.enemiesEngine:Update()
end
end
end
end
-function br:ObjectManager()
- if br.engines.OM_Engine == nil then
- br.engines.OM_Engine = br._G.CreateFrame("Frame", nil, br._G.UIParent)
- br.engines.OM_Engine:SetScript("OnUpdate", ObjectManagerUpdate)
- br.engines.OM_Engine:Show()
+function engines:ObjectManager()
+ if engines.OM_Engine == nil then
+ engines.OM_Engine = br._G.CreateFrame("Frame", nil, br._G.UIParent)
+ engines.OM_Engine:SetScript("OnUpdate", ObjectManagerUpdate)
+ engines.OM_Engine:Show()
end
end
-- Object Tracker
-local function ObjectTrackerUpdate(self)
+local function ObjectTrackerUpdate(self, elapsed)
if br.unlocked then
- if br.data ~= nil and br.data.settings ~= nil and br.data.settings[br.selectedSpec] ~= nil
- and br.data.settings[br.selectedSpec].toggles ~= nil
+ if br.data ~= nil and br.data.settings ~= nil and br.data.settings[br.loader.selectedSpec] ~= nil
+ and br.data.settings[br.loader.selectedSpec].toggles ~= nil
then
- if br.data.settings[br.selectedSpec].toggles["Power"] ~= nil
- and br.data.settings[br.selectedSpec].toggles["Power"] == 1
+ if br.data.settings[br.loader.selectedSpec].toggles["Power"] ~= nil
+ and br.data.settings[br.loader.selectedSpec].toggles["Power"] == 1
then
+ self.trackerElapsed = (self.trackerElapsed or 0) + (elapsed or 0)
+ local trackerInterval = math.max(0.10, engines:getUpdateRate())
+ if self.trackerElapsed < trackerInterval then
+ return
+ end
+ self.trackerElapsed = 0
-- Tracker
- br.objectTracker()
+ engines.tracker:objectTracker()
end
end
end
end
-function br:ObjectTracker()
- if br.engines.Tracker_Engine == nil then
- br.engines.Tracker_Engine = br._G.CreateFrame("Frame", nil, br._G.UIParent)
- br.engines.Tracker_Engine:SetScript("OnUpdate", ObjectTrackerUpdate)
- br.engines.Tracker_Engine:Show()
+function engines:ObjectTracker()
+ if engines.Tracker_Engine == nil then
+ engines.Tracker_Engine = br._G.CreateFrame("Frame", nil, br._G.UIParent)
+ engines.Tracker_Engine:SetScript("OnUpdate", ObjectTrackerUpdate)
+ engines.Tracker_Engine:Show()
end
end
local function updateRotationOnSpecChange()
br.ui:closeWindow("all")
- br:saveSettings(nil, nil, br.selectedSpec, br.selectedProfileName)
+ br.ui.settingsManagement:saveSettings(nil, nil, br.loader.selectedSpec, br.loader.selectedProfileName)
-- Update Selected Spec/Profile
- br.selectedSpec = select(2, br._G.GetSpecializationInfo(br._G.GetSpecialization()))
- br.selectedSpecID = br._G.GetSpecializationInfo(br._G.GetSpecialization())
- br.loader.loadProfiles()
- br.loadLastProfileTracker()
+ br.loader.selectedSpec = select(2, br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization()))
+ br.loader.selectedSpecID = br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization())
+ br.loader.cBuilder:loadProfiles()
+ local specID = br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization())
+ if not (br.loader.noRotationsFound and br.loader.noRotationsFound[specID]) then
+ br.ui.settingsManagement:loadLastProfileTracker()
+ end
br.data.loadedSettings = false
-- Load Default Settings
- br:defaultSettings()
+ br.ui.settingsManagement:defaultSettings()
br.rotationChanged = true
- br._G.wipe(br.commandHelp)
- br:slashHelpList()
+ br._G.wipe(br.slashCommands.commandHelp)
+ br.slashCommands:slashHelpList()
end
-local collectGarbage = true
-function br.BadRotationsUpdate(self)
+-- collectGarbage should only be set when rotation explicitly changes
+local collectGarbage = false
+local initialized = false
+function engines:Update(self)
local startTime = br._G.debugprofilestop()
local LibDraw = br._G.LibStub("LibDraw-BR")
local Print = br._G["print"]
-- Check for Unlocker
if not br.unlocked then
- br.unlocked = br:loadUnlockerAPI()
+ br.unlocked = br.unlockers:loadUnlockerAPI()
end
if br.disablePulse == true then
return
@@ -111,30 +123,41 @@ function br.BadRotationsUpdate(self)
-- Load and Cycle BR
-- Notify Not Unlocked
br.ui:closeWindow("all")
- br.ChatOverlay("Unable To Load")
- if br.isChecked("Notify Not Unlocked") and
- br.timer:useTimer("notLoaded", br.getOptionValue("Notify Not Unlocked")) then
- Print(
- "|cffFFFFFFCannot Start... |cffFF1100BR |cffFFFFFFcan not complete loading. Please check requirements.")
+ br.ui.chatOverlay:Show("Unable To Load")
+ if br.functions.misc:isChecked("Notify Not Unlocked") and
+ br.debug.timer:useTimer("notLoaded", br.functions.misc:getOptionValue("Notify Not Unlocked"))
+ then
+ Print("|cffFFFFFFCannot Start... |cffFF1100BR |cffFFFFFFcan not complete loading. Please check requirements.")
end
return false
- elseif br.unlocked and br._G.GetObjectCount() ~= nil then
- br.devMode()
+ elseif br.unlocked then--and br._G.GetObjectCount() ~= nil then
+ -- Load Expansion Spell Lists
+ if br.lists.spells == nil then
+ br.lists:loadExpansionSpells()
+ end
+ br.functions.misc:devMode()
-- Check BR Out of Date
- -- br:checkBrOutOfDate()
- -- Get Current Addon Name
- br:setAddonName()
+ -- br.unlockers:checkBrOutOfDate()
-- Load Saved Settings
- br:loadSavedSettings()
+ br.ui.settingsManagement:loadSavedSettings()
+ -- Initialization Complete
+ if not initialized then
+ br.ui.chatOverlay:Show("-= BadRotations Loaded =-")
+ br._G.print("Initialization Complete.")
+ initialized = true
+ end
+ -- Cache settings check
+ local settings = br.data and br.data.settings and br.data.settings[br.loader.selectedSpec]
+ local toggles = settings and settings.toggles
-- Continue Load
- if br.data ~= nil and br.data.settings ~= nil and br.data.settings[br.selectedSpec] ~= nil and br.data.settings[br.selectedSpec].toggles ~= nil then
+ if toggles then
-- BR Main Toggle Off
- if br.data.settings[br.selectedSpec].toggles["Power"] ~= nil and br.data.settings[br.selectedSpec].toggles["Power"] ~= 1 then
+ if toggles["Power"] ~= nil and toggles["Power"] ~= 1 then
-- BR Main Toggle On - Main Cycle
-- Clear Queue
if br.player ~= nil and br.player.queue ~= nil and #br.player.queue ~= 0 then
br._G.wipe(br.player.queue)
- if not br.isChecked("Mute Queue") then
+ if not br.functions.misc:isChecked("Mute Queue") then
Print("BR Disabled! - Queue Cleared.")
end
end
@@ -143,13 +166,12 @@ function br.BadRotationsUpdate(self)
-- Clear All Tracking
LibDraw.clearCanvas()
return false
- elseif br.timer:useTimer("playerUpdate", br:getUpdateRate()) then
- -- Set Fall Distance
- br.fallDist = br.getFallDistance() or 0
+ elseif br.debug.timer:useTimer("playerUpdate", br.engines:getUpdateRate()) then
-- Quaking helper
- if br.getOptionCheck("Quaking Helper") then
+ if br.functions.misc:getOptionCheck("Quaking Helper") then
+ local quakingID = br.functions.spell:getHighestSpellID("quaking") or 240448
if (br._G.UnitChannelInfo("player") or br._G.UnitCastingInfo("player")) and
- br.getDebuffRemain("player", 240448) < 0.5 and br.getDebuffRemain("player", 240448) > 0 then
+ br.functions.aura:getDebuffRemain("player", quakingID) < 0.5 and br.functions.aura:getDebuffRemain("player", quakingID) > 0 then
br._G.RunMacroText("/stopcasting")
end
end
@@ -159,18 +181,18 @@ function br.BadRotationsUpdate(self)
br._G.CastSpellByID(br.botSpell, br.botUnit)
br.castID = false
end
- local playerSpec = br._G.GetSpecializationInfo(br._G.GetSpecialization())
+ local playerSpec = br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization())
if playerSpec == "" then
playerSpec = "Initial"
end
-- Initialize Player
- if br.player == nil or br.player.profile ~= br.selectedSpec or br.rotationChanged then
+ if br.player == nil or br.player.profile ~= br.loader.selectedSpec or br.rotationChanged then
-- Load Last Profile Tracker
-- br:loadLastProfileTracker()
- br.selectedProfile = br.data.settings[br.selectedSpec]["RotationDrop"] or 1
+ br.loader.selectedProfile = br.data.settings[br.loader.selectedSpec]["RotationDrop"] or 1
-- Load Profile
-- br.loaded = false
- br.player = br.loader:new(playerSpec, br.selectedSpec)
+ br.player = br.loader.cBuilder:new(playerSpec, br.loader.selectedSpec)
if br.player ~= nil then
setmetatable(br.player, {
__index = br.loader
@@ -181,31 +203,32 @@ function br.BadRotationsUpdate(self)
br.player:update()
if br.rotationChanged then
- br:saveLastProfileTracker()
- br:loadSettings(nil, br.player.class, br.selectedSpec, br.selectedProfileName)
+ br.ui.settingsManagement:saveLastProfileTracker()
+ br.ui.settingsManagement:loadSettings(nil, br.player.class, br.loader.selectedSpec, br.loader.selectedProfileName)
br.ui:closeWindow("profile")
br.player:createOptions()
br.player:createToggles()
+ collectGarbage = true
end
- collectGarbage = true
br.rotationChanged = false
+ br._G.print("Profile Load Complete")
end
end
-- Queue Casting
- if (br.isChecked("Queue Casting") or (br.player ~= nil and br.player.queue ~= 0)) and
+ if (br.functions.misc:isChecked("Queue Casting") or (br.player ~= nil and br.player.queue ~= 0)) and
not br._G.UnitChannelInfo("player") then
- if br.castQueue() then
+ if br.functions.cast:castQueue() then
return
end
end
- if (not br.isChecked("Queue Casting") or br.GetUnitIsDeadOrGhost("player") or
+ if (not br.functions.misc:isChecked("Queue Casting") or br.functions.unit:GetUnitIsDeadOrGhost("player") or
not br._G.UnitAffectingCombat("player")) and br.player ~= nil and #br.player.queue ~= 0 then
br._G.wipe(br.player.queue)
- if not br.isChecked("Mute Queue") then
- if not br.isChecked("Queue Casting") then
+ if not br.functions.misc:isChecked("Mute Queue") then
+ if not br.functions.misc:isChecked("Queue Casting") then
Print("Queue System Disabled! - Queue Cleared.")
end
- if br.GetUnitIsDeadOrGhost("player") then
+ if br.functions.unit:GetUnitIsDeadOrGhost("player") then
Print("Player Death Detected! - Queue Cleared.")
end
if not br._G.UnitAffectingCombat("player") then
@@ -214,71 +237,71 @@ function br.BadRotationsUpdate(self)
end
end
-- Smart Queue
- if br.unlocked and br.isChecked("Smart Queue") then
- br.smartQueue()
+ if br.unlocked and br.functions.misc:isChecked("Smart Queue") then
+ br.functions.SmartQueue:smartQueue()
end
-- Update Player
- if br.player ~= nil and (not br._G.CanExitVehicle() or (br.GetUnitExists("target") and br.getDistance("target") < 5)) then
+ if br.player ~= nil and (not br._G.CanExitVehicle() or (br.functions.unit:GetUnitExists("target") and br.functions.range:getDistance("target") < 5)) then
br.player:update()
end
-- Automatic catch the pig
- if br.getOptionCheck("Freehold - Pig Catcher") or br.getOptionCheck("De Other Side - Bomb Snatcher") then
- br.bossHelper()
+ if br.functions.misc:getOptionCheck("Freehold - Pig Catcher") or br.functions.misc:getOptionCheck("De Other Side - Bomb Snatcher") then
+ br.misc.Zu:bossHelper()
end
-- Healing Engine
- if br.isChecked("HE Active") then
- br.friend:Update()
+ if br.functions.misc:isChecked("HE Active") then
+ br.engines.healingEngine.friend:Update()
end
-- Auto Loot
- br.autoLoot()
- -- Close windows and swap br.selectedSpec on Spec Change
- local thisSpec = select(2, br._G.GetSpecializationInfo(br._G.GetSpecialization()))
- if thisSpec ~= "" and thisSpec ~= br.selectedSpec then
+ br.engines.lootEngine:autoLoot()
+ -- Close windows and swap br.loader.selectedSpec on Spec Change
+ local thisSpec = select(2, br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization()))
+ if thisSpec ~= "" and thisSpec ~= br.loader.selectedSpec then
updateRotationOnSpecChange()
end
-- Show Main Button
- if br.data.settings ~= nil and br.data.settings[br.selectedSpec].toggles["Main"] ~= 1 and
- br.data.settings[br.selectedSpec].toggles["Main"] ~= 0 then
+ if br.data.settings ~= nil and br.data.settings[br.loader.selectedSpec].toggles["Main"] ~= 1 and
+ br.data.settings[br.loader.selectedSpec].toggles["Main"] ~= 0 then
if not br._G.UnitAffectingCombat("player") then
- br.data.settings[br.selectedSpec].toggles["Main"] = 1
- br.mainButton:Show()
+ br.data.settings[br.loader.selectedSpec].toggles["Main"] = 1
+ br.ui.toggles.mainButton:Show()
end
end
-- Display Distance on Main Icon
- if br.mainButton ~= nil then
- local targetDistance = br.getDistance("target") or 0
+ if br.ui.toggles.mainButton ~= nil then
+ local targetDistance = br.functions.range:getDistance("target") or 0
local displayDistance = math.ceil(targetDistance)
- br.mainText:SetText(displayDistance)
+ br.ui.toggles.mainText:SetText(displayDistance)
end
-- LoS Line Draw
- if br.isChecked("Healer Line of Sight Indicator") then
- br.inLoSHealer()
+ if br.functions.misc:isChecked("Healer Line of Sight Indicator") then
+ br.engines.healingEngineFunctions:inLoSHealer()
end
-- Get DBM/BigWigs Timer/Bars
- -- global -> br.DBM.Timer
+ -- global -> br.functions.DBM.Timer
if br._G.C_AddOns.IsAddOnLoaded("DBM-Core") then
- br.DBM:getBars()
+ br.functions.DBM:getBars()
elseif br._G.C_AddOns.IsAddOnLoaded("BigWigs") then
- if not br.DBM.BigWigs then
- br.BWInit()
+ if not br.functions.DBM.BigWigs then
+ br.functions.custom:BWInit()
else
- br.BWCheck()
+ br.functions.custom:BWCheck()
end
end
-- Accept dungeon queues
- br:AcceptQueues()
+ br.functions.misc:AcceptQueues()
-- Fishing
- br.fishing()
+ br.misc.ProfessionHelper:fishing()
-- Profession Helper
- br.ProfessionHelper()
+ br.misc.ProfessionHelper:ProfessionHelper()
-- Rotation Log
br.ui:toggleDebugWindow()
- -- Settings Garbage Collection
- if not br.loadFile and collectGarbage then
+ -- Collect Garbage - only when rotation actually changed, not on every profile init
+ if collectGarbage then
-- Ensure we have all the settings recorded
br.ui:recreateWindows()
- -- Delete old settings
- br:cleanSettings()
+ -- Delete old settings only when rotation changed to prevent wiping on toggle
+ br.ui.settingsManagement:cleanSettings()
-- Set flag to prevent un-needed runs
collectGarbage = false
end
@@ -286,6 +309,6 @@ function br.BadRotationsUpdate(self)
elseif br.player ~= nil then
updateRotationOnSpecChange()
end -- End Settings Loaded Check
- end -- End Unlock Check
+ end -- End Unlock Check
br.debug.cpu:updateDebug(startTime, "pulse")
-end -- End Bad Rotations Update Function
+end -- End Bad Rotations Update Function
diff --git a/System/Debugging.lua b/System/Debugging.lua
index e523580413..7287e71151 100644
--- a/System/Debugging.lua
+++ b/System/Debugging.lua
@@ -1,6 +1,14 @@
local _, br = ...
-- Provides functions to help debugging and profiling
+-- Custom Debug Print
+function br.debug:print(message)
+ if br.data.settings[br.loader.selectedSpec].toggles["isDebugging"] == true then
+ br._G.print(message)
+ end
+end
+
+-- CPU Debug
br.debug.cpu = {}
br.debug.cpu.healingEngine = {
UnitName = 0,
@@ -31,7 +39,7 @@ function br.debug.cpu:updateDebug(startTime, table)
averageTime = 0
}
end
- if br.isChecked("Debug Timers") then
+ if br.functions.misc:isChecked("Debug Timers") then
br.debug.cpu[table].totalIterations = br.debug.cpu[table].totalIterations + 1
br.debug.cpu[table].currentTime = endTime - startTime
br.debug.cpu[table].elapsedTime = br.debug.cpu[table].elapsedTime + br.debug.cpu[table].currentTime
@@ -47,26 +55,26 @@ end
function br.debug.cpu:getHealingEngine()
local usage, calls
- usage, calls = br._G.GetFunctionCPUUsage(br.friend.Update, true)
- br.debug.cpu.healingEngine["br.friend_Update"] = {usage = usage, calls = calls}
+ usage, calls = br._G.GetFunctionCPUUsage(br.engines.healingEngine.friend.Update, true)
+ br.debug.cpu.healingEngine["br.engines.healingEngine.friend_Update"] = {usage = usage, calls = calls}
- usage, calls = br._G.GetFunctionCPUUsage(br.friend.UpdateUnit, true)
- br.debug.cpu.healingEngine["br.friend_UpdateUnit"] = {usage = usage, calls = calls}
+ usage, calls = br._G.GetFunctionCPUUsage(br.engines.healingEngine.friend.UpdateUnit, true)
+ br.debug.cpu.healingEngine["br.engines.healingEngine.friend_UpdateUnit"] = {usage = usage, calls = calls}
--local tmpUsage, tmpCalls
- --for i=1, #br.friend do
- -- usage, calls = GetFunctionCPUUsage(br.friend[i].UpdateUnit, true)
+ --for i=1, #br.engines.healingEngine.friend do
+ -- usage, calls = GetFunctionCPUUsage(br.engines.healingEngine.friend[i].UpdateUnit, true)
-- tmpUsage = tmpUsage + usage
-- tmpCalls = tmpCalls + calls
- -- br.debug.cpu.healingEngine["br.friend_UpdateUnit"] = {usage = usage, calls = calls }
- -- br.friend[i]:UpdateUnit()
+ -- br.debug.cpu.healingEngine["br.engines.healingEngine.friend_UpdateUnit"] = {usage = usage, calls = calls }
+ -- br.engines.healingEngine.friend[i]:UpdateUnit()
--end
-- usage, calls = GetFunctionCPUUsage(, true)
end
--- Get Execution Speed
-- Prints the time needed to run a function X times
-function br.debug.getEXspeed(cycles, func)
+function br.debug:getEXspeed(cycles, func)
local startTime = br._G.debugprofilestop()
for i = 1, cycles do
@@ -80,8 +88,8 @@ end
-- INTO TIMER LUA
-br.timer = {}
-function br.timer:useTimer(timerName, interval)
+br.debug.timer = {}
+function br.debug.timer:useTimer(timerName, interval)
if self[timerName] == nil then
self[timerName] = 0
end
@@ -93,8 +101,8 @@ function br.timer:useTimer(timerName, interval)
end
end
---[[br.timer = {}
-function br.timer:useTimer(timerName, interval, randomPercent)
+--[[br.debug.timer = {}
+function br.debug.timer:useTimer(timerName, interval, randomPercent)
local randomPercent = randomPercent or 0
if randomPercent > 0 then
local randomRange = interval * randomPercent / 100
diff --git a/System/Expansions/classic.lua b/System/Expansions/classic.lua
new file mode 100644
index 0000000000..274f12966e
--- /dev/null
+++ b/System/Expansions/classic.lua
@@ -0,0 +1,442 @@
+-- Classic API Compatibility Layer
+-- Handles API differences in Classic
+local _, br = ...
+
+if not br.isClassic then return end
+
+local api = br.api.wow
+
+-- IsPassiveSpell wrapper
+-- Classic: C_Spell.IsSpellPassive or fallback to global IsPassiveSpell
+api.IsPassiveSpell = function(spellID)
+ if _G.C_Spell and _G.C_Spell.IsSpellPassive then
+ return _G.C_Spell.IsSpellPassive(spellID)
+ end
+ if _G.IsPassiveSpell then
+ return _G.IsPassiveSpell(spellID)
+ end
+ return false
+end
+
+-- GetSpellInfo wrapper
+-- Classic: C_Spell.GetSpellInfo returns a table (like Retail)
+-- Returns: name, rank, iconID, castTime, minRange, maxRange, spellID, originalIconID
+-- NOTE: Classic API has minRange/maxRange swapped in the table!
+api.GetSpellInfo = function(spellIdentifier)
+ if not spellIdentifier then return nil end
+
+ -- GetSpellInfo doesn't accept tables, only numbers or strings
+ if type(spellIdentifier) == "table" then return nil end
+
+ -- Try C_Spell first (Classic has this like Retail)
+ if _G.C_Spell and _G.C_Spell.GetSpellInfo then
+ local spellInfo = _G.C_Spell.GetSpellInfo(spellIdentifier)
+ if spellInfo and type(spellInfo) == "table" then
+ -- CLASSIC BUG FIX: minRange and maxRange are swapped in the Classic API
+ -- spellInfo.minRange actually contains maxRange, and vice versa
+ return spellInfo.name,
+ nil,
+ spellInfo.iconID,
+ spellInfo.castTime,
+ spellInfo.maxRange, -- Swapped: use maxRange field for minRange
+ spellInfo.minRange, -- Swapped: use minRange field for maxRange
+ spellInfo.spellID,
+ spellInfo.originalIconID
+ end
+ end
+
+ -- Fallback to global GetSpellInfo if C_Spell not available
+ if _G.GetSpellInfo then
+ return _G.GetSpellInfo(spellIdentifier)
+ end
+
+ return nil
+end
+
+-- GetAuraDataByIndex wrapper (for C_UnitAuras compatibility)
+-- Classic: Convert UnitAura to auraData table format
+api.GetAuraDataByIndex = function(unit, index, filter)
+ if _G.C_UnitAuras and _G.C_UnitAuras.GetAuraDataByIndex then
+ return _G.C_UnitAuras.GetAuraDataByIndex(unit, index, filter)
+ end
+
+ -- Fallback: use UnitAura and convert to auraData format
+ local name, icon, count, dispelType, duration, expirationTime, source, isStealable,
+ nameplateShowPersonal, spellId, canApplyAura, isBossDebuff, castByPlayer,
+ nameplateShowAll, timeMod = _G.UnitAura(unit, index, filter)
+
+ if not name then return nil end
+
+ return {
+ applications = count,
+ auraInstanceID = 0,
+ canApplyAura = canApplyAura,
+ charges = 0,
+ dispelName = dispelType,
+ duration = duration,
+ expirationTime = expirationTime,
+ icon = icon,
+ isBossAura = isBossDebuff,
+ isFromPlayerOrPlayerPet = castByPlayer,
+ isHarmful = false,
+ isHelpful = true,
+ isNameplateOnly = nameplateShowPersonal,
+ isRaid = false,
+ isStealable = isStealable,
+ name = name,
+ nameplateShowAll = nameplateShowAll,
+ nameplateShowPersonal = nameplateShowPersonal,
+ points = {},
+ sourceUnit = source,
+ spellId = spellId,
+ timeMod = timeMod
+ }
+end
+
+-- GetDebuffDataByIndex wrapper
+-- Classic: Convert UnitDebuff to debuffData format
+api.GetDebuffDataByIndex = function(unit, index, filter)
+ if _G.C_UnitAuras and _G.C_UnitAuras.GetDebuffDataByIndex then
+ return _G.C_UnitAuras.GetDebuffDataByIndex(unit, index, filter)
+ end
+
+ local name, icon, count, dispelType, duration, expirationTime, source, isStealable,
+ nameplateShowPersonal, spellId, canApplyAura, isBossDebuff, castByPlayer,
+ nameplateShowAll, timeMod = _G.UnitDebuff(unit, index, filter)
+
+ if not name then return nil end
+
+ return {
+ applications = count,
+ auraInstanceID = 0,
+ canApplyAura = canApplyAura,
+ charges = 0,
+ dispelName = dispelType,
+ duration = duration,
+ expirationTime = expirationTime,
+ icon = icon,
+ isBossAura = isBossDebuff,
+ isFromPlayerOrPlayerPet = castByPlayer,
+ isHarmful = true,
+ isHelpful = false,
+ isNameplateOnly = nameplateShowPersonal,
+ isRaid = false,
+ isStealable = isStealable,
+ name = name,
+ nameplateShowAll = nameplateShowAll,
+ nameplateShowPersonal = nameplateShowPersonal,
+ points = {},
+ sourceUnit = source,
+ spellId = spellId,
+ timeMod = timeMod
+ }
+end
+
+-- GetBuffDataByIndex wrapper
+-- Classic: Convert UnitBuff to buffData format
+api.GetBuffDataByIndex = function(unit, index, filter)
+ if _G.C_UnitAuras and _G.C_UnitAuras.GetBuffDataByIndex then
+ return _G.C_UnitAuras.GetBuffDataByIndex(unit, index, filter)
+ end
+
+ local name, icon, count, dispelType, duration, expirationTime, source, isStealable,
+ nameplateShowPersonal, spellId, canApplyAura, isBossDebuff, castByPlayer,
+ nameplateShowAll, timeMod = _G.UnitBuff(unit, index, filter)
+
+ if not name then return nil end
+
+ return {
+ applications = count,
+ auraInstanceID = 0,
+ canApplyAura = canApplyAura,
+ charges = 0,
+ dispelName = dispelType,
+ duration = duration,
+ expirationTime = expirationTime,
+ icon = icon,
+ isBossAura = isBossDebuff,
+ isFromPlayerOrPlayerPet = castByPlayer,
+ isHarmful = false,
+ isHelpful = true,
+ isNameplateOnly = nameplateShowPersonal,
+ isRaid = false,
+ isStealable = isStealable,
+ name = name,
+ nameplateShowAll = nameplateShowAll,
+ nameplateShowPersonal = nameplateShowPersonal,
+ points = {},
+ sourceUnit = source,
+ spellId = spellId,
+ timeMod = timeMod
+ }
+end
+
+-- GetPlayerAuraBySpellID wrapper
+-- Classic: Iterate through player buffs/debuffs to find by spell ID
+api.GetPlayerAuraBySpellID = function(spellID)
+ if _G.C_UnitAuras and _G.C_UnitAuras.GetPlayerAuraBySpellID then
+ return _G.C_UnitAuras.GetPlayerAuraBySpellID(spellID)
+ end
+
+ -- Check buffs first
+ for i = 1, 40 do
+ local name, icon, count, dispelType, duration, expirationTime, source, isStealable,
+ nameplateShowPersonal, buffSpellId, canApplyAura, isBossDebuff, castByPlayer,
+ nameplateShowAll, timeMod = _G.UnitBuff("player", i)
+
+ if not name then break end
+ if buffSpellId == spellID then
+ return {
+ applications = count,
+ auraInstanceID = 0,
+ canApplyAura = canApplyAura,
+ charges = 0,
+ dispelName = dispelType,
+ duration = duration,
+ expirationTime = expirationTime,
+ icon = icon,
+ isBossAura = isBossDebuff,
+ isFromPlayerOrPlayerPet = castByPlayer,
+ isHarmful = false,
+ isHelpful = true,
+ name = name,
+ spellId = buffSpellId,
+ timeMod = timeMod
+ }
+ end
+ end
+
+ -- Check debuffs
+ for i = 1, 40 do
+ local name, icon, count, dispelType, duration, expirationTime, source, isStealable,
+ nameplateShowPersonal, debuffSpellId, canApplyAura, isBossDebuff, castByPlayer,
+ nameplateShowAll, timeMod = _G.UnitDebuff("player", i)
+
+ if not name then break end
+ if debuffSpellId == spellID then
+ return {
+ applications = count,
+ auraInstanceID = 0,
+ canApplyAura = canApplyAura,
+ charges = 0,
+ dispelName = dispelType,
+ duration = duration,
+ expirationTime = expirationTime,
+ icon = icon,
+ isBossAura = isBossDebuff,
+ isFromPlayerOrPlayerPet = castByPlayer,
+ isHarmful = true,
+ isHelpful = false,
+ name = name,
+ spellId = debuffSpellId,
+ timeMod = timeMod
+ }
+ end
+ end
+
+ return nil
+end
+
+-- FindAuraByName wrapper
+-- TBC: Use AuraUtil.FindAuraByName
+api.FindAuraByName = function(spellName, unit, filter)
+ -- Prefer the native AuraUtil if present
+ if _G.AuraUtil and _G.AuraUtil.FindAuraByName then
+ return _G.AuraUtil.FindAuraByName(spellName, unit, filter)
+ end
+
+ -- Fallback: try to find the aura by scanning buff/debuff lists
+ if not spellName or not unit then return nil end
+
+ local upFilter = filter and br._G.strupper(filter) or nil
+ local hasPlayer = upFilter and br._G.strfind(upFilter, "PLAYER")
+ local hasHelpful = upFilter and br._G.strfind(upFilter, "HELPFUL")
+ local hasHarmful = upFilter and br._G.strfind(upFilter, "HARMFUL")
+
+ local function scanForAura(spellName, unit, filterType, auraType)
+ for i = 1, 40 do
+ local aura = (hasHelpful or auraType=="HELPFUL") and api.GetBuffDataByIndex(unit, i, filterType)
+ or api.GetDebuffDataByIndex(unit, i, filterType)
+ if not aura then break end
+ if aura.name and aura.name == spellName then return aura end
+ end
+ return nil
+ end
+
+ -- If an explicit filter is provided, prefer using it when scanning
+ if upFilter then
+ if hasHelpful then
+ scanForAura(spellName, unit, filter, "HELPFUL")
+ end
+
+ if hasHarmful then
+ scanForAura(spellName, unit, filter, "HARMFUL")
+ end
+
+ -- If only PLAYER specified (no HELPFUL/HARMFUL), scan both with PLAYER filter
+ if hasPlayer and not hasHelpful and not hasHarmful then
+ scanForAura(spellName, unit, "PLAYER", "HELPFUL")
+ scanForAura(spellName, unit, "PLAYER", "HARMFUL")
+ end
+ else
+ -- No filter provided: scan buffs then debuffs
+ scanForAura(spellName, unit, nil, "HELPFUL")
+ scanForAura(spellName, unit, nil, "HARMFUL")
+ end
+
+ -- Last resort: generic GetAuraDataByIndex scan
+ for i = 1, 40 do
+ local aura = api.GetAuraDataByIndex(unit, i, filter)
+ if not aura then break end
+ if aura.name == spellName then return aura end
+ end
+
+ return nil
+end
+
+-- Tooltip Hook Compatibility
+-- Classic: Use OnTooltipSetSpell script hook
+api.HookTooltipSetSpell = function(callback)
+ if GameTooltip.HookScript then
+ GameTooltip:HookScript("OnTooltipSetSpell", function(self)
+ local _, id = self:GetSpell()
+ if id then
+ callback(self, id)
+ end
+ end)
+ end
+end
+
+-- Classic: Use OnTooltipSetUnit script hook
+api.HookTooltipSetUnit = function(callback)
+ if GameTooltip.HookScript then
+ GameTooltip:HookScript("OnTooltipSetUnit", function(self)
+ callback(self)
+ end)
+ end
+end
+
+-- Classic: Use OnTooltipSetItem script hook
+api.HookTooltipSetItem = function(tooltip, callback)
+ if tooltip and tooltip.HookScript then
+ tooltip:HookScript("OnTooltipSetItem", function(self)
+ callback(self)
+ end)
+ end
+end
+
+-- UnitInPhase wrapper
+-- Classic: Use the global UnitInPhase function if available
+api.UnitInPhase = function(unit)
+ if _G.UnitInPhase then
+ return _G.UnitInPhase(unit)
+ end
+ -- Fallback: assume in phase if unit exists
+ if _G.UnitExists then
+ return _G.UnitExists(unit)
+ end
+ return true
+end
+
+-- GetNumQuestLogEntries wrapper
+-- Classic: Use the global GetNumQuestLogEntries function
+api.GetNumQuestLogEntries = function()
+ if _G.GetNumQuestLogEntries then
+ return _G.GetNumQuestLogEntries()
+ end
+ return 0
+end
+
+-- GetQuestLogTitle wrapper
+-- Classic: Use the global GetQuestLogTitle function
+api.GetQuestLogTitle = function(questLogIndex)
+ if _G.GetQuestLogTitle then
+ return _G.GetQuestLogTitle(questLogIndex)
+ end
+ return nil
+end
+
+-- Talent System - Classic uses tier/column system
+api.getTalentInfo = function(spec, spellTalents)
+ local talents = {}
+
+ -- -- Initial profiles (spec > 1400) don't have talents
+ -- if spec > 1400 then return talents end
+
+ -- local activeSpecGroup = _G.C_SpecializationInfo and _G.C_SpecializationInfo.GetActiveSpecGroup and _G.C_SpecializationInfo.GetActiveSpecGroup()
+ -- if not activeSpecGroup then return talents end
+
+ -- -- Classic talents: 7 tiers x 3 columns
+ -- for tier = 1, 7 do
+ -- for column = 1, 3 do
+ -- local info = _G.C_SpecializationInfo and _G.C_SpecializationInfo.GetTalentInfo and
+ -- _G.C_SpecializationInfo.GetTalentInfo({ tier = tier, column = column, specializationIndex = activeSpecGroup })
+ -- if info and info.spellID then
+ -- talents[info.spellID] = {
+ -- active = info.selected and true or false,
+ -- rank = 0, -- Classic talents don't expose rank
+ -- tier = tier,
+ -- column = column,
+ -- }
+ -- end
+ -- end
+ -- end
+
+ return talents
+end
+
+-- GCD Spell List for Classic
+-- In Classic, spell 61304 doesn't exist, so we need class-specific spells to check GCD
+local GCDSpells = {
+ DRUID = {
+ NONE = 5176, -- Wrath
+ CAT = 5221 -- Shred
+ },
+ HUNTER = 1978, -- Serpent Sting
+ MAGE = 133, -- Fireball
+ PALADIN = 635, -- Holy Light
+ PRIEST = 2050, -- Holy Smite (Lesser Heal)
+ ROGUE = 1752, -- Sinister Strike
+ SHAMAN = 403, -- Lightning Bolt
+ WARLOCK = 686, -- Shadow Bolt
+ WARRIOR = 772 -- Rend
+}
+
+-- GetGCDSpellID - Returns the appropriate spell ID to check GCD for Classic
+-- This replaces the Retail spell 61304 which doesn't exist in Classic
+api.GetGCDSpellID = function()
+ local _, class = _G.UnitClass("player")
+ if not class then return 5176 end -- Default to Wrath if class unknown
+
+ local gcdSpell = GCDSpells[class]
+
+ -- Handle Druid form-specific GCD spells
+ if class == "DRUID" and type(gcdSpell) == "table" then
+ local form = _G.GetShapeshiftForm and _G.GetShapeshiftForm()
+ if form == 1 then -- Cat Form
+ return gcdSpell.CAT
+ else
+ return gcdSpell.NONE
+ end
+ end
+
+ return gcdSpell or 5176 -- Return class spell or default
+end
+
+--FindBaseSpellByID wrapper
+-- TBC: Use the global FindBaseSpellByID function if available
+api.FindBaseSpellByID = function(spellID)
+ if C_SpellBook and C_SpellBook.FindBaseSpellByID then
+ return C_SpellBook.FindBaseSpellByID(spellID)
+ end
+ if _G.FindBaseSpellByID then
+ return _G.FindBaseSpellByID(spellID)
+ end
+ return spellID
+end
+
+-- Print version info on load
+if br.api.compat.getVersionInfo then
+ local versionInfo = br.api.compat:getVersionInfo()
+ print("|cffA330C9BadRotations|r |cffFFFFFFAPI:|r Classic compatibility layer loaded (Build: " .. versionInfo.build .. ")")
+end
diff --git a/System/Expansions/mop.lua b/System/Expansions/mop.lua
new file mode 100644
index 0000000000..84382c3b76
--- /dev/null
+++ b/System/Expansions/mop.lua
@@ -0,0 +1,401 @@
+-- Mists of Pandaria Classic API Compatibility Layer
+-- Handles API differences in MoP Classic
+local _, br = ...
+
+if not br.isMOP then return end
+
+local api = br.api.wow
+
+-- IsPassiveSpell wrapper
+-- MoP: C_Spell.IsSpellPassive or fallback to global IsPassiveSpell
+api.IsPassiveSpell = function(spellID)
+ if _G.C_Spell and _G.C_Spell.IsSpellPassive then
+ return _G.C_Spell.IsSpellPassive(spellID)
+ end
+ if _G.IsPassiveSpell then
+ return _G.IsPassiveSpell(spellID)
+ end
+ return false
+end
+
+-- GetSpellInfo wrapper
+-- MoP Classic: C_Spell.GetSpellInfo returns a table (like Retail)
+-- Returns: name, rank, iconID, castTime, minRange, maxRange, spellID, originalIconID
+api.GetSpellInfo = function(spellIdentifier)
+ if not spellIdentifier then return nil end
+
+ -- GetSpellInfo doesn't accept tables, only numbers or strings
+ if type(spellIdentifier) == "table" then return nil end
+
+ -- Try C_Spell first (MoP Classic has this like Retail)
+ if _G.C_Spell and _G.C_Spell.GetSpellInfo then
+ local spellInfo = _G.C_Spell.GetSpellInfo(spellIdentifier)
+ if spellInfo and type(spellInfo) == "table" then
+ return spellInfo.name,
+ nil,
+ spellInfo.iconID,
+ spellInfo.castTime,
+ spellInfo.minRange,
+ spellInfo.maxRange,
+ spellInfo.spellID,
+ spellInfo.originalIconID
+ end
+ end
+
+ -- Fallback to global GetSpellInfo if C_Spell not available
+ if _G.GetSpellInfo then
+ return _G.GetSpellInfo(spellIdentifier)
+ end
+
+ return nil
+end
+
+-- GetAuraDataByIndex wrapper (for C_UnitAuras compatibility)
+-- MoP Classic: Convert UnitAura to auraData table format
+api.GetAuraDataByIndex = function(unit, index, filter)
+ if _G.C_UnitAuras and _G.C_UnitAuras.GetAuraDataByIndex then
+ return _G.C_UnitAuras.GetAuraDataByIndex(unit, index, filter)
+ end
+
+ -- Fallback: use UnitAura and convert to auraData format
+ local name, icon, count, dispelType, duration, expirationTime, source, isStealable,
+ nameplateShowPersonal, spellId, canApplyAura, isBossDebuff, castByPlayer,
+ nameplateShowAll, timeMod = _G.UnitAura(unit, index, filter)
+
+ if not name then return nil end
+
+ return {
+ applications = count,
+ auraInstanceID = 0,
+ canApplyAura = canApplyAura,
+ charges = 0,
+ dispelName = dispelType,
+ duration = duration,
+ expirationTime = expirationTime,
+ icon = icon,
+ isBossAura = isBossDebuff,
+ isFromPlayerOrPlayerPet = castByPlayer,
+ isHarmful = false,
+ isHelpful = true,
+ isNameplateOnly = nameplateShowPersonal,
+ isRaid = false,
+ isStealable = isStealable,
+ name = name,
+ nameplateShowAll = nameplateShowAll,
+ nameplateShowPersonal = nameplateShowPersonal,
+ points = {},
+ sourceUnit = source,
+ spellId = spellId,
+ timeMod = timeMod
+ }
+end
+
+-- GetDebuffDataByIndex wrapper
+-- MoP Classic: Convert UnitDebuff to debuffData format
+api.GetDebuffDataByIndex = function(unit, index, filter)
+ if _G.C_UnitAuras and _G.C_UnitAuras.GetDebuffDataByIndex then
+ return _G.C_UnitAuras.GetDebuffDataByIndex(unit, index, filter)
+ end
+
+ local name, icon, count, dispelType, duration, expirationTime, source, isStealable,
+ nameplateShowPersonal, spellId, canApplyAura, isBossDebuff, castByPlayer,
+ nameplateShowAll, timeMod = _G.UnitDebuff(unit, index, filter)
+
+ if not name then return nil end
+
+ return {
+ applications = count,
+ auraInstanceID = 0,
+ canApplyAura = canApplyAura,
+ charges = 0,
+ dispelName = dispelType,
+ duration = duration,
+ expirationTime = expirationTime,
+ icon = icon,
+ isBossAura = isBossDebuff,
+ isFromPlayerOrPlayerPet = castByPlayer,
+ isHarmful = true,
+ isHelpful = false,
+ isNameplateOnly = nameplateShowPersonal,
+ isRaid = false,
+ isStealable = isStealable,
+ name = name,
+ nameplateShowAll = nameplateShowAll,
+ nameplateShowPersonal = nameplateShowPersonal,
+ points = {},
+ sourceUnit = source,
+ spellId = spellId,
+ timeMod = timeMod
+ }
+end
+
+-- GetBuffDataByIndex wrapper
+-- MoP Classic: Convert UnitBuff to buffData format
+api.GetBuffDataByIndex = function(unit, index, filter)
+ if _G.C_UnitAuras and _G.C_UnitAuras.GetBuffDataByIndex then
+ return _G.C_UnitAuras.GetBuffDataByIndex(unit, index, filter)
+ end
+
+ local name, icon, count, dispelType, duration, expirationTime, source, isStealable,
+ nameplateShowPersonal, spellId, canApplyAura, isBossDebuff, castByPlayer,
+ nameplateShowAll, timeMod = _G.UnitBuff(unit, index, filter)
+
+ if not name then return nil end
+
+ return {
+ applications = count,
+ auraInstanceID = 0,
+ canApplyAura = canApplyAura,
+ charges = 0,
+ dispelName = dispelType,
+ duration = duration,
+ expirationTime = expirationTime,
+ icon = icon,
+ isBossAura = isBossDebuff,
+ isFromPlayerOrPlayerPet = castByPlayer,
+ isHarmful = false,
+ isHelpful = true,
+ isNameplateOnly = nameplateShowPersonal,
+ isRaid = false,
+ isStealable = isStealable,
+ name = name,
+ nameplateShowAll = nameplateShowAll,
+ nameplateShowPersonal = nameplateShowPersonal,
+ points = {},
+ sourceUnit = source,
+ spellId = spellId,
+ timeMod = timeMod
+ }
+end
+
+-- GetPlayerAuraBySpellID wrapper
+-- MoP Classic: Iterate through player buffs/debuffs to find by spell ID
+api.GetPlayerAuraBySpellID = function(spellID)
+ if _G.C_UnitAuras and _G.C_UnitAuras.GetPlayerAuraBySpellID then
+ return _G.C_UnitAuras.GetPlayerAuraBySpellID(spellID)
+ end
+
+ -- Check buffs first
+ for i = 1, 40 do
+ local name, icon, count, dispelType, duration, expirationTime, source, isStealable,
+ nameplateShowPersonal, buffSpellId, canApplyAura, isBossDebuff, castByPlayer,
+ nameplateShowAll, timeMod = _G.UnitBuff("player", i)
+
+ if not name then break end
+ if buffSpellId == spellID then
+ return {
+ applications = count,
+ auraInstanceID = 0,
+ canApplyAura = canApplyAura,
+ charges = 0,
+ dispelName = dispelType,
+ duration = duration,
+ expirationTime = expirationTime,
+ icon = icon,
+ isBossAura = isBossDebuff,
+ isFromPlayerOrPlayerPet = castByPlayer,
+ isHarmful = false,
+ isHelpful = true,
+ name = name,
+ spellId = buffSpellId,
+ timeMod = timeMod
+ }
+ end
+ end
+
+ -- Check debuffs
+ for i = 1, 40 do
+ local name, icon, count, dispelType, duration, expirationTime, source, isStealable,
+ nameplateShowPersonal, debuffSpellId, canApplyAura, isBossDebuff, castByPlayer,
+ nameplateShowAll, timeMod = _G.UnitDebuff("player", i)
+
+ if not name then break end
+ if debuffSpellId == spellID then
+ return {
+ applications = count,
+ auraInstanceID = 0,
+ canApplyAura = canApplyAura,
+ charges = 0,
+ dispelName = dispelType,
+ duration = duration,
+ expirationTime = expirationTime,
+ icon = icon,
+ isBossAura = isBossDebuff,
+ isFromPlayerOrPlayerPet = castByPlayer,
+ isHarmful = true,
+ isHelpful = false,
+ name = name,
+ spellId = debuffSpellId,
+ timeMod = timeMod
+ }
+ end
+ end
+
+ return nil
+end
+
+-- FindAuraByName wrapper
+-- TBC: Use AuraUtil.FindAuraByName
+api.FindAuraByName = function(spellName, unit, filter)
+ -- Prefer the native AuraUtil if present
+ if _G.AuraUtil and _G.AuraUtil.FindAuraByName then
+ return _G.AuraUtil.FindAuraByName(spellName, unit, filter)
+ end
+
+ -- Fallback: try to find the aura by scanning buff/debuff lists
+ if not spellName or not unit then return nil end
+
+ local upFilter = filter and br._G.strupper(filter) or nil
+ local hasPlayer = upFilter and br._G.strfind(upFilter, "PLAYER")
+ local hasHelpful = upFilter and br._G.strfind(upFilter, "HELPFUL")
+ local hasHarmful = upFilter and br._G.strfind(upFilter, "HARMFUL")
+
+ local function scanForAura(spellName, unit, filterType, auraType)
+ for i = 1, 40 do
+ local aura = (hasHelpful or auraType=="HELPFUL") and api.GetBuffDataByIndex(unit, i, filterType)
+ or api.GetDebuffDataByIndex(unit, i, filterType)
+ if not aura then break end
+ if aura.name and aura.name == spellName then return aura end
+ end
+ return nil
+ end
+
+ -- If an explicit filter is provided, prefer using it when scanning
+ if upFilter then
+ if hasHelpful then
+ scanForAura(spellName, unit, filter, "HELPFUL")
+ end
+
+ if hasHarmful then
+ scanForAura(spellName, unit, filter, "HARMFUL")
+ end
+
+ -- If only PLAYER specified (no HELPFUL/HARMFUL), scan both with PLAYER filter
+ if hasPlayer and not hasHelpful and not hasHarmful then
+ scanForAura(spellName, unit, "PLAYER", "HELPFUL")
+ scanForAura(spellName, unit, "PLAYER", "HARMFUL")
+ end
+ else
+ -- No filter provided: scan buffs then debuffs
+ scanForAura(spellName, unit, nil, "HELPFUL")
+ scanForAura(spellName, unit, nil, "HARMFUL")
+ end
+
+ -- Last resort: generic GetAuraDataByIndex scan
+ for i = 1, 40 do
+ local aura = api.GetAuraDataByIndex(unit, i, filter)
+ if not aura then break end
+ if aura.name == spellName then return aura end
+ end
+
+ return nil
+end
+
+-- Tooltip Hook Compatibility
+-- MoP Classic: Use OnTooltipSetSpell script hook
+api.HookTooltipSetSpell = function(callback)
+ if GameTooltip.HookScript then
+ GameTooltip:HookScript("OnTooltipSetSpell", function(self)
+ local _, id = self:GetSpell()
+ if id then
+ callback(self, id)
+ end
+ end)
+ end
+end
+
+-- MoP Classic: Use OnTooltipSetUnit script hook
+api.HookTooltipSetUnit = function(callback)
+ if GameTooltip.HookScript then
+ GameTooltip:HookScript("OnTooltipSetUnit", function(self)
+ callback(self)
+ end)
+ end
+end
+
+-- MoP Classic: Use OnTooltipSetItem script hook
+api.HookTooltipSetItem = function(tooltip, callback)
+ if tooltip and tooltip.HookScript then
+ tooltip:HookScript("OnTooltipSetItem", function(self)
+ callback(self)
+ end)
+ end
+end
+
+-- UnitInPhase wrapper
+-- MoP Classic: Use the global UnitInPhase function if available
+api.UnitInPhase = function(unit)
+ if _G.UnitInPhase then
+ return _G.UnitInPhase(unit)
+ end
+ -- Fallback: assume in phase if unit exists
+ if _G.UnitExists then
+ return _G.UnitExists(unit)
+ end
+ return true
+end
+
+-- GetNumQuestLogEntries wrapper
+-- MoP Classic: Use the global GetNumQuestLogEntries function
+api.GetNumQuestLogEntries = function()
+ if _G.GetNumQuestLogEntries then
+ return _G.GetNumQuestLogEntries()
+ end
+ return 0
+end
+
+-- GetQuestLogTitle wrapper
+-- MoP Classic: Use the global GetQuestLogTitle function
+api.GetQuestLogTitle = function(questLogIndex)
+ if _G.GetQuestLogTitle then
+ return _G.GetQuestLogTitle(questLogIndex)
+ end
+ return nil
+end
+
+-- Talent System - MoP/Classic uses tier/column system
+api.getTalentInfo = function(spec, spellTalents)
+ local talents = {}
+
+ -- Initial profiles (spec > 1400) don't have talents
+ if spec > 1400 then return talents end
+
+ local activeSpecGroup = _G.C_SpecializationInfo and _G.C_SpecializationInfo.GetActiveSpecGroup and _G.C_SpecializationInfo.GetActiveSpecGroup()
+ if not activeSpecGroup then return talents end
+
+ -- MoP/Classic talents: 7 tiers x 3 columns
+ for tier = 1, 7 do
+ for column = 1, 3 do
+ local info = _G.C_SpecializationInfo and _G.C_SpecializationInfo.GetTalentInfo and
+ _G.C_SpecializationInfo.GetTalentInfo({ tier = tier, column = column, specializationIndex = activeSpecGroup })
+ if info and info.spellID then
+ talents[info.spellID] = {
+ active = info.selected and true or false,
+ rank = 0, -- Classic talents don't expose rank
+ tier = tier,
+ column = column,
+ }
+ end
+ end
+ end
+
+ return talents
+end
+
+--FindBaseSpellByID wrapper
+-- TBC: Use the global FindBaseSpellByID function if available
+api.FindBaseSpellByID = function(spellID)
+ if C_SpellBook and C_SpellBook.FindBaseSpellByID then
+ return C_SpellBook.FindBaseSpellByID(spellID)
+ end
+ if _G.FindBaseSpellByID then
+ return _G.FindBaseSpellByID(spellID)
+ end
+ return spellID
+end
+
+-- Print version info on load
+if br.api.compat.getVersionInfo then
+ local versionInfo = br.api.compat:getVersionInfo()
+ print("|cffA330C9BadRotations|r |cffFFFFFFAPI:|r MoP Classic compatibility layer loaded (Build: " .. versionInfo.build .. ")")
+end
diff --git a/System/Expansions/retail.lua b/System/Expansions/retail.lua
new file mode 100644
index 0000000000..0bf05871c5
--- /dev/null
+++ b/System/Expansions/retail.lua
@@ -0,0 +1,438 @@
+-- Retail WoW API Compatibility Layer
+-- Handles API differences in Retail (Dragonflight/The War Within/etc.)
+local _, br = ...
+
+if not br.isRetail then return end
+
+local api = br.api.wow
+
+-- GetSpellInfo wrapper
+-- Retail: C_Spell.GetSpellInfo returns a table
+-- Returns: name, rank, iconID, castTime, minRange, maxRange, spellID, originalIconID
+api.GetSpellInfo = function(spellIdentifier)
+ if not spellIdentifier then return nil end
+
+ -- C_Spell.GetSpellInfo doesn't accept tables, only numbers or strings
+ if type(spellIdentifier) == "table" then return nil end
+
+ if C_Spell and C_Spell.GetSpellInfo then
+ local spellInfo = C_Spell.GetSpellInfo(spellIdentifier)
+ if spellInfo and type(spellInfo) == "table" then
+ return spellInfo.name, nil, spellInfo.iconID, spellInfo.castTime,
+ spellInfo.minRange, spellInfo.maxRange, spellInfo.spellID,
+ spellInfo.originalIconID
+ end
+ end
+
+ return nil
+end
+
+-- IsPassiveSpell wrapper
+-- Retail: C_Spell.IsSpellPassive
+api.IsPassiveSpell = function(spellID)
+ if _G.C_Spell and _G.C_Spell.IsSpellPassive then
+ return _G.C_Spell.IsSpellPassive(spellID)
+ end
+ return false
+end
+
+-- GetSpellLink wrapper
+-- Retail: C_Spell.GetSpellLink
+api.GetSpellLink = function(spellID)
+ if C_Spell and C_Spell.GetSpellLink then
+ return C_Spell.GetSpellLink(spellID)
+ end
+ return nil
+end
+
+-- GetSpellName wrapper
+-- Retail: C_Spell.GetSpellName
+api.GetSpellName = function(spellID)
+ if C_Spell and C_Spell.GetSpellName then
+ return C_Spell.GetSpellName(spellID)
+ end
+ return nil
+end
+
+-- GetSpellDescription wrapper
+-- Retail: C_Spell.GetSpellDescription
+api.GetSpellDescription = function(spellID)
+ if C_Spell and C_Spell.GetSpellDescription then
+ return C_Spell.GetSpellDescription(spellID)
+ end
+ return nil
+end
+
+-- GetSpellCooldown wrapper
+-- Retail: C_Spell.GetSpellCooldown returns a table
+api.GetSpellCooldown = function(spellID)
+ if C_Spell and C_Spell.GetSpellCooldown then
+ return C_Spell.GetSpellCooldown(spellID)
+ end
+ return nil
+end
+
+-- GetSpellCharges wrapper
+-- Retail: C_Spell.GetSpellCharges returns a table
+api.GetSpellCharges = function(spellID)
+ if C_Spell and C_Spell.GetSpellCharges then
+ return C_Spell.GetSpellCharges(spellID)
+ end
+ return nil
+end
+
+-- IsSpellInRange wrapper
+-- Retail: C_Spell.IsSpellInRange
+api.IsSpellInRange = function(spellID, unit)
+ if C_Spell and C_Spell.IsSpellInRange then
+ return C_Spell.IsSpellInRange(spellID, unit)
+ end
+ return nil
+end
+
+-- IsSpellUsable wrapper
+-- Retail: C_Spell.IsSpellUsable returns a table
+api.IsSpellUsable = function(spellID)
+ if C_Spell and C_Spell.IsSpellUsable then
+ return C_Spell.IsSpellUsable(spellID)
+ end
+ return nil
+end
+
+-- IsSpellPassive wrapper
+-- Retail: C_Spell.IsSpellPassive
+api.IsSpellPassive = function(spellID)
+ if C_Spell and C_Spell.IsSpellPassive then
+ return C_Spell.IsSpellPassive(spellID)
+ end
+ return nil
+end
+
+-- IsSpellHelpful wrapper
+-- Retail: C_Spell.IsSpellHelpful
+api.IsSpellHelpful = function(spellID)
+ if C_Spell and C_Spell.IsSpellHelpful then
+ return C_Spell.IsSpellHelpful(spellID)
+ end
+ return nil
+end
+
+-- IsSpellHarmful wrapper
+-- Retail: C_Spell.IsSpellHarmful
+api.IsSpellHarmful = function(spellID)
+ if C_Spell and C_Spell.IsSpellHarmful then
+ return C_Spell.IsSpellHarmful(spellID)
+ end
+ return nil
+end
+
+-- SpellHasRange wrapper
+-- Retail: C_Spell.SpellHasRange
+api.SpellHasRange = function(spellID)
+ if C_Spell and C_Spell.SpellHasRange then
+ return C_Spell.SpellHasRange(spellID)
+ end
+ return nil
+end
+
+-- GetSpellBookItemInfo wrapper
+-- Retail: C_SpellBook.GetSpellBookItemInfo returns a table and requires numerical slot
+-- Classic could accept spell names, but Retail cannot - return nil for non-numeric slots
+api.GetSpellBookItemInfo = function(slot, bookType)
+ -- Retail API requires a numerical slot, not a spell name
+ if type(slot) ~= "number" then
+ return nil
+ end
+
+ if C_SpellBook and C_SpellBook.GetSpellBookItemInfo then
+ return C_SpellBook.GetSpellBookItemInfo(slot, bookType)
+ end
+ return nil
+end
+
+-- GetItemInfo wrapper
+-- Retail: GetItemInfo still works but may return nil initially, requiring cache
+api.GetItemInfo = function(itemID)
+ return GetItemInfo(itemID)
+end
+
+-- GetItemCooldown wrapper
+-- Retail: C_Container.GetItemCooldown
+api.GetItemCooldown = function(itemID)
+ if C_Container and C_Container.GetItemCooldown then
+ return C_Container.GetItemCooldown(itemID)
+ end
+ return nil
+end
+
+-- UnitAura wrapper
+-- Retail: C_UnitAuras.GetAuraDataByIndex, returns table
+api.UnitAura = function(unit, index, filter)
+ if C_UnitAuras and C_UnitAuras.GetAuraDataByIndex then
+ local auraData = C_UnitAuras.GetAuraDataByIndex(unit, index, filter)
+ if auraData and AuraUtil and AuraUtil.UnpackAuraData then
+ return AuraUtil.UnpackAuraData(auraData)
+ end
+ return nil
+ end
+ return nil
+end
+
+-- UnitBuff wrapper (uses UnitAura in Retail)
+api.UnitBuff = function(unit, index, filter)
+ return api.UnitAura(unit, index, filter)
+end
+
+-- UnitDebuff wrapper (uses UnitAura in Retail)
+api.UnitDebuff = function(unit, index, filter)
+ return api.UnitAura(unit, index, filter)
+end
+
+-- FindAuraByName wrapper
+-- TBC: Use AuraUtil.FindAuraByName
+api.FindAuraByName = function(spellName, unit, filter)
+ -- Prefer the native AuraUtil if present
+ if _G.AuraUtil and _G.AuraUtil.FindAuraByName then
+ return _G.AuraUtil.FindAuraByName(spellName, unit, filter)
+ end
+
+ -- Fallback: try to find the aura by scanning buff/debuff lists
+ if not spellName or not unit then return nil end
+
+ local upFilter = filter and br._G.strupper(filter) or nil
+ local hasPlayer = upFilter and br._G.strfind(upFilter, "PLAYER")
+ local hasHelpful = upFilter and br._G.strfind(upFilter, "HELPFUL")
+ local hasHarmful = upFilter and br._G.strfind(upFilter, "HARMFUL")
+
+ local function scanForAura(spellName, unit, filterType, auraType)
+ for i = 1, 40 do
+ local aura = (hasHelpful or auraType=="HELPFUL") and api.GetBuffDataByIndex(unit, i, filterType)
+ or api.GetDebuffDataByIndex(unit, i, filterType)
+ if not aura then break end
+ if aura.name and aura.name == spellName then return aura end
+ end
+ return nil
+ end
+
+ -- If an explicit filter is provided, prefer using it when scanning
+ if upFilter then
+ if hasHelpful then
+ scanForAura(spellName, unit, filter, "HELPFUL")
+ end
+
+ if hasHarmful then
+ scanForAura(spellName, unit, filter, "HARMFUL")
+ end
+
+ -- If only PLAYER specified (no HELPFUL/HARMFUL), scan both with PLAYER filter
+ if hasPlayer and not hasHelpful and not hasHarmful then
+ scanForAura(spellName, unit, "PLAYER", "HELPFUL")
+ scanForAura(spellName, unit, "PLAYER", "HARMFUL")
+ end
+ else
+ -- No filter provided: scan buffs then debuffs
+ scanForAura(spellName, unit, nil, "HELPFUL")
+ scanForAura(spellName, unit, nil, "HARMFUL")
+ end
+
+ -- Last resort: generic GetAuraDataByIndex scan
+ for i = 1, 40 do
+ local aura = api.GetAuraDataByIndex(unit, i, filter)
+ if not aura then break end
+ if aura.name == spellName then return aura end
+ end
+
+ return nil
+end
+
+-- GetAuraDataByIndex wrapper
+-- Retail: C_UnitAuras.GetAuraDataByIndex
+api.GetAuraDataByIndex = function(unit, index, filter)
+ if _G.C_UnitAuras and _G.C_UnitAuras.GetAuraDataByIndex then
+ return _G.C_UnitAuras.GetAuraDataByIndex(unit, index, filter)
+ end
+ return nil
+end
+
+-- GetDebuffDataByIndex wrapper
+-- Retail: C_UnitAuras.GetDebuffDataByIndex
+api.GetDebuffDataByIndex = function(unit, index, filter)
+ if _G.C_UnitAuras and _G.C_UnitAuras.GetDebuffDataByIndex then
+ return _G.C_UnitAuras.GetDebuffDataByIndex(unit, index, filter)
+ end
+ return nil
+end
+
+-- GetBuffDataByIndex wrapper
+-- Retail: C_UnitAuras.GetBuffDataByIndex
+api.GetBuffDataByIndex = function(unit, index, filter)
+ if _G.C_UnitAuras and _G.C_UnitAuras.GetBuffDataByIndex then
+ return _G.C_UnitAuras.GetBuffDataByIndex(unit, index, filter)
+ end
+ return nil
+end
+
+-- GetPlayerAuraBySpellID wrapper
+-- Retail: C_UnitAuras.GetPlayerAuraBySpellID
+api.GetPlayerAuraBySpellID = function(spellID)
+ if _G.C_UnitAuras and _G.C_UnitAuras.GetPlayerAuraBySpellID then
+ return _G.C_UnitAuras.GetPlayerAuraBySpellID(spellID)
+ end
+ return nil
+end
+
+-- Tooltip Hook Compatibility
+-- Retail: Use TooltipDataProcessor instead of OnTooltipSetSpell
+api.HookTooltipSetSpell = function(callback)
+ if TooltipDataProcessor and TooltipDataProcessor.AddTooltipPostCall then
+ TooltipDataProcessor.AddTooltipPostCall(Enum.TooltipDataType.Spell, function(tooltip, data)
+ if data and data.id then
+ callback(tooltip, data.id)
+ end
+ end)
+ end
+end
+
+-- Retail: Use TooltipDataProcessor for unit tooltips
+api.HookTooltipSetUnit = function(callback)
+ if TooltipDataProcessor and TooltipDataProcessor.AddTooltipPostCall then
+ TooltipDataProcessor.AddTooltipPostCall(Enum.TooltipDataType.Unit, function(tooltip)
+ callback(tooltip)
+ end)
+ end
+end
+
+-- Retail: Use TooltipDataProcessor for item tooltips
+api.HookTooltipSetItem = function(tooltip, callback)
+ if TooltipDataProcessor and TooltipDataProcessor.AddTooltipPostCall then
+ TooltipDataProcessor.AddTooltipPostCall(Enum.TooltipDataType.Item, function(tooltipFrame)
+ if tooltipFrame == tooltip or not tooltip then
+ callback(tooltipFrame)
+ end
+ end)
+ end
+end
+
+-- UnitInPhase wrapper
+-- Retail: May not exist or use UnitPhaseReason
+api.UnitInPhase = function(unit)
+ -- In Retail, units are generally always in phase unless there's a specific phase issue
+ -- Try UnitPhaseReason first if it exists
+ if _G.UnitPhaseReason then
+ local phaseReason = _G.UnitPhaseReason(unit)
+ -- phaseReason: nil = same phase, 0 = different phase, other values indicate issues
+ return not phaseReason or phaseReason == 0
+ end
+ -- Fallback: assume in phase if unit exists
+ if _G.UnitExists then
+ return _G.UnitExists(unit)
+ end
+ return true
+end
+
+-- GetNumQuestLogEntries wrapper
+-- Retail: Use C_QuestLog.GetNumQuestLogEntries
+api.GetNumQuestLogEntries = function()
+ if C_QuestLog and C_QuestLog.GetNumQuestLogEntries then
+ return C_QuestLog.GetNumQuestLogEntries()
+ end
+ return 0
+end
+
+-- GetQuestLogTitle wrapper
+-- Retail: Use C_QuestLog.GetInfo which returns a table
+-- Classic: GetQuestLogTitle returns: title, level, suggestedGroup, isHeader, isCollapsed, isComplete, frequency, questID, ...
+api.GetQuestLogTitle = function(questLogIndex)
+ if C_QuestLog and C_QuestLog.GetInfo then
+ local info = C_QuestLog.GetInfo(questLogIndex)
+ if info then
+ -- Return in classic format: title, level, suggestedGroup, isHeader, isCollapsed, isComplete, frequency, questID
+ return info.title, info.level, info.suggestedGroup, info.isHeader, info.isCollapsed,
+ info.isComplete, info.frequency, info.questID, nil, info.questID,
+ info.isOnMap, info.hasLocalPOI, info.isTask, info.isStory
+ end
+ end
+ return nil
+end
+
+-- Print version info on load
+if br.api.compat.getVersionInfo then
+ local versionInfo = br.api.compat:getVersionInfo()
+ print("|cffA330C9BadRotations|r |cffFFFFFFAPI:|r Retail compatibility layer loaded (Build: " .. versionInfo.build .. ")")
+end
+
+-- Talent System - Retail uses C_Traits API (Dragonflight talent trees)
+api.getTalentInfo = function(spec, spellTalents)
+ local talents = {}
+
+ -- Initial profiles (spec > 1400) don't have talents
+ if spec > 1400 then return talents end
+
+ local configId = _G.C_ClassTalents and _G.C_ClassTalents.GetActiveConfigID and _G.C_ClassTalents.GetActiveConfigID()
+ if not configId then return talents end
+
+ local configInfo = _G.C_Traits and _G.C_Traits.GetConfigInfo and _G.C_Traits.GetConfigInfo(configId)
+ if not configInfo then return talents end
+
+ -- Helper to get active talents from a node
+ local function getActiveTalents(node, configId)
+ local activeTalents = {}
+ if not node.entryIDsWithCommittedRanks then return activeTalents end
+
+ for _, entryID in pairs(node.entryIDsWithCommittedRanks) do
+ local entryInfo = _G.C_Traits.GetEntryInfo(configId, entryID)
+ if entryInfo and entryInfo.definitionID then
+ local definitionInfo = _G.C_Traits.GetDefinitionInfo(entryInfo.definitionID)
+ if definitionInfo and definitionInfo.spellID then
+ activeTalents[definitionInfo.spellID] = {
+ active = true,
+ rank = node.activeRank or 0
+ }
+ end
+ end
+ end
+ return activeTalents
+ end
+
+ -- Traverse all talent trees
+ for _, treeId in pairs(configInfo.treeIDs) do
+ local nodes = _G.C_Traits.GetTreeNodes(treeId)
+ if nodes then
+ for _, nodeId in pairs(nodes) do
+ local node = _G.C_Traits.GetNodeInfo(configId, nodeId)
+ if node then
+ local activeTalents = getActiveTalents(node, configId)
+
+ -- Process all entries in this node
+ for _, entryID in pairs(node.entryIDs or {}) do
+ local entryInfo = _G.C_Traits.GetEntryInfo(configId, entryID)
+ if entryInfo and entryInfo.definitionID then
+ local definitionInfo = _G.C_Traits.GetDefinitionInfo(entryInfo.definitionID)
+ if definitionInfo and definitionInfo.spellID then
+ local talentID = definitionInfo.spellID
+ talents[talentID] = {
+ active = activeTalents[talentID] and true or false,
+ rank = activeTalents[talentID] and activeTalents[talentID].rank or 0,
+ }
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ return talents
+end
+
+--FindBaseSpellByID wrapper
+-- TBC: Use the global FindBaseSpellByID function if available
+api.FindBaseSpellByID = function(spellID)
+ if C_SpellBook and C_SpellBook.FindBaseSpellByID then
+ return C_SpellBook.FindBaseSpellByID(spellID)
+ end
+ if _G.FindBaseSpellByID then
+ return _G.FindBaseSpellByID(spellID)
+ end
+ return spellID
+end
diff --git a/System/Expansions/tbc.lua b/System/Expansions/tbc.lua
new file mode 100644
index 0000000000..d6ca2808d5
--- /dev/null
+++ b/System/Expansions/tbc.lua
@@ -0,0 +1,455 @@
+-- TBC API Compatibility Layer
+-- Handles API differences in TBC
+local _, br = ...
+
+if not br.isBC then return end
+
+local api = br.api.wow
+
+-- IsPassiveSpell wrapper
+-- TBC: C_Spell.IsSpellPassive or fallback to global IsPassiveSpell
+api.IsPassiveSpell = function(spellID)
+ if _G.C_Spell and _G.C_Spell.IsSpellPassive then
+ return _G.C_Spell.IsSpellPassive(spellID)
+ end
+ if _G.IsPassiveSpell then
+ return _G.IsPassiveSpell(spellID)
+ end
+ return false
+end
+
+-- GetSpellInfo wrapper
+-- TBC: C_Spell.GetSpellInfo returns a table (like Retail)
+-- Returns: name, rank, iconID, castTime, minRange, maxRange, spellID, originalIconID
+-- NOTE: TBC API has minRange/maxRange swapped in the table!
+api.GetSpellInfo = function(spellIdentifier)
+ if not spellIdentifier then return nil end
+
+ -- GetSpellInfo doesn't accept tables, only numbers or strings
+ if type(spellIdentifier) == "table" then return nil end
+
+ -- Try C_Spell first (TBC has this like Retail)
+ if _G.C_Spell and _G.C_Spell.GetSpellInfo then
+ local spellInfo = _G.C_Spell.GetSpellInfo(spellIdentifier)
+ if spellInfo and type(spellInfo) == "table" then
+ -- TBC BUG FIX: minRange and maxRange are swapped in the TBC API
+ -- spellInfo.minRange actually contains maxRange, and vice versa
+ return spellInfo.name,
+ nil,
+ spellInfo.iconID,
+ spellInfo.castTime,
+ spellInfo.maxRange, -- Swapped: use maxRange field for minRange
+ spellInfo.minRange, -- Swapped: use minRange field for maxRange
+ spellInfo.spellID,
+ spellInfo.originalIconID
+ end
+ end
+
+ -- Fallback to global GetSpellInfo if C_Spell not available
+ if _G.GetSpellInfo then
+ return _G.GetSpellInfo(spellIdentifier)
+ end
+
+ return nil
+end
+
+-- GetAuraDataByIndex wrapper (for C_UnitAuras compatibility)
+-- TBC: Convert UnitAura to auraData table format
+api.GetAuraDataByIndex = function(unit, index, filter)
+ if _G.C_UnitAuras and _G.C_UnitAuras.GetAuraDataByIndex then
+ return _G.C_UnitAuras.GetAuraDataByIndex(unit, index, filter)
+ end
+
+ -- Fallback: use UnitAura and convert to auraData format
+ local name, icon, count, dispelType, duration, expirationTime, source, isStealable,
+ nameplateShowPersonal, spellId, canApplyAura, isBossDebuff, castByPlayer,
+ nameplateShowAll, timeMod = _G.UnitAura(unit, index, filter)
+
+ if not name then return nil end
+
+ return {
+ applications = count,
+ auraInstanceID = 0,
+ canApplyAura = canApplyAura,
+ charges = 0,
+ dispelName = dispelType,
+ duration = duration,
+ expirationTime = expirationTime,
+ icon = icon,
+ isBossAura = isBossDebuff,
+ isFromPlayerOrPlayerPet = castByPlayer,
+ isHarmful = false,
+ isHelpful = true,
+ isNameplateOnly = nameplateShowPersonal,
+ isRaid = false,
+ isStealable = isStealable,
+ name = name,
+ nameplateShowAll = nameplateShowAll,
+ nameplateShowPersonal = nameplateShowPersonal,
+ points = {},
+ sourceUnit = source,
+ spellId = spellId,
+ timeMod = timeMod
+ }
+end
+
+-- GetDebuffDataByIndex wrapper
+-- TBC: Convert UnitDebuff to debuffData format
+api.GetDebuffDataByIndex = function(unit, index, filter)
+ if _G.C_UnitAuras and _G.C_UnitAuras.GetDebuffDataByIndex then
+ return _G.C_UnitAuras.GetDebuffDataByIndex(unit, index, filter)
+ end
+
+ local name, icon, count, dispelType, duration, expirationTime, source, isStealable,
+ nameplateShowPersonal, spellId, canApplyAura, isBossDebuff, castByPlayer,
+ nameplateShowAll, timeMod = _G.UnitDebuff(unit, index, filter)
+
+ if not name then return nil end
+
+ return {
+ applications = count,
+ auraInstanceID = 0,
+ canApplyAura = canApplyAura,
+ charges = 0,
+ dispelName = dispelType,
+ duration = duration,
+ expirationTime = expirationTime,
+ icon = icon,
+ isBossAura = isBossDebuff,
+ isFromPlayerOrPlayerPet = castByPlayer,
+ isHarmful = true,
+ isHelpful = false,
+ isNameplateOnly = nameplateShowPersonal,
+ isRaid = false,
+ isStealable = isStealable,
+ name = name,
+ nameplateShowAll = nameplateShowAll,
+ nameplateShowPersonal = nameplateShowPersonal,
+ points = {},
+ sourceUnit = source,
+ spellId = spellId,
+ timeMod = timeMod
+ }
+end
+
+-- GetBuffDataByIndex wrapper
+-- TBC: Convert UnitBuff to buffData format
+api.GetBuffDataByIndex = function(unit, index, filter)
+ if _G.C_UnitAuras and _G.C_UnitAuras.GetBuffDataByIndex then
+ return _G.C_UnitAuras.GetBuffDataByIndex(unit, index, filter)
+ end
+
+ local name, icon, count, dispelType, duration, expirationTime, source, isStealable,
+ nameplateShowPersonal, spellId, canApplyAura, isBossDebuff, castByPlayer,
+ nameplateShowAll, timeMod = _G.UnitBuff(unit, index, filter)
+
+ if not name then return nil end
+
+ return {
+ applications = count,
+ auraInstanceID = 0,
+ canApplyAura = canApplyAura,
+ charges = 0,
+ dispelName = dispelType,
+ duration = duration,
+ expirationTime = expirationTime,
+ icon = icon,
+ isBossAura = isBossDebuff,
+ isFromPlayerOrPlayerPet = castByPlayer,
+ isHarmful = false,
+ isHelpful = true,
+ isNameplateOnly = nameplateShowPersonal,
+ isRaid = false,
+ isStealable = isStealable,
+ name = name,
+ nameplateShowAll = nameplateShowAll,
+ nameplateShowPersonal = nameplateShowPersonal,
+ points = {},
+ sourceUnit = source,
+ spellId = spellId,
+ timeMod = timeMod
+ }
+end
+
+-- GetPlayerAuraBySpellID wrapper
+-- TBC: Iterate through player buffs/debuffs to find by spell ID
+api.GetPlayerAuraBySpellID = function(spellID)
+ if type(spellID) == "table" then
+ -- return highest rank spell that is known
+ for i = #spellID, 1, -1 do
+ if br.functions.spell:isKnown(spellID[i]) then
+ spellID = spellID[i]
+ break
+ end
+ end
+ end
+
+ if _G.C_UnitAuras and _G.C_UnitAuras.GetPlayerAuraBySpellID then
+ return _G.C_UnitAuras.GetPlayerAuraBySpellID(spellID)
+ end
+
+ -- Check buffs first
+ for i = 1, 40 do
+ local name, icon, count, dispelType, duration, expirationTime, source, isStealable,
+ nameplateShowPersonal, buffSpellId, canApplyAura, isBossDebuff, castByPlayer,
+ nameplateShowAll, timeMod = _G.UnitBuff("player", i)
+
+ if not name then break end
+ if buffSpellId == spellID then
+ return {
+ applications = count,
+ auraInstanceID = 0,
+ canApplyAura = canApplyAura,
+ charges = 0,
+ dispelName = dispelType,
+ duration = duration,
+ expirationTime = expirationTime,
+ icon = icon,
+ isBossAura = isBossDebuff,
+ isFromPlayerOrPlayerPet = castByPlayer,
+ isHarmful = false,
+ isHelpful = true,
+ name = name,
+ spellId = buffSpellId,
+ timeMod = timeMod
+ }
+ end
+ end
+
+ -- Check debuffs
+ for i = 1, 40 do
+ local name, icon, count, dispelType, duration, expirationTime, source, isStealable,
+ nameplateShowPersonal, debuffSpellId, canApplyAura, isBossDebuff, castByPlayer,
+ nameplateShowAll, timeMod = _G.UnitDebuff("player", i)
+
+ if not name then break end
+ if debuffSpellId == spellID then
+ return {
+ applications = count,
+ auraInstanceID = 0,
+ canApplyAura = canApplyAura,
+ charges = 0,
+ dispelName = dispelType,
+ duration = duration,
+ expirationTime = expirationTime,
+ icon = icon,
+ isBossAura = isBossDebuff,
+ isFromPlayerOrPlayerPet = castByPlayer,
+ isHarmful = true,
+ isHelpful = false,
+ name = name,
+ spellId = debuffSpellId,
+ timeMod = timeMod
+ }
+ end
+ end
+
+ return nil
+end
+
+-- FindAuraByName wrapper
+-- TBC: Use AuraUtil.FindAuraByName
+api.FindAuraByName = function(spellName, unit, filter)
+ -- Prefer the native AuraUtil if present
+ if not br.isBC and _G.AuraUtil and _G.AuraUtil.FindAuraByName then
+ return _G.AuraUtil.FindAuraByName(spellName, unit, filter)
+ end
+
+ -- Fallback: try to find the aura by scanning buff/debuff lists
+ if not spellName or not unit then return nil end
+
+ local upFilter = filter and br._G.strupper(filter) or nil
+ local hasPlayer = upFilter and br._G.strfind(upFilter, "PLAYER")
+ local hasHelpful = upFilter and br._G.strfind(upFilter, "HELPFUL")
+ local hasHarmful = upFilter and br._G.strfind(upFilter, "HARMFUL")
+
+ local function scanForAura(spellName, unit, filterType, auraType)
+ for i = 1, 40 do
+ local aura = (hasHelpful or auraType=="HELPFUL") and api.GetBuffDataByIndex(unit, i, filterType)
+ or api.GetDebuffDataByIndex(unit, i, filterType)
+ if not aura then break end
+ if aura.name and aura.name == spellName then return aura end
+ end
+ return nil
+ end
+
+ -- If an explicit filter is provided, prefer using it when scanning
+ if upFilter then
+ if hasHelpful then
+ scanForAura(spellName, unit, filter, "HELPFUL")
+ end
+
+ if hasHarmful then
+ scanForAura(spellName, unit, filter, "HARMFUL")
+ end
+
+ -- If only PLAYER specified (no HELPFUL/HARMFUL), scan both with PLAYER filter
+ if hasPlayer and not hasHelpful and not hasHarmful then
+ scanForAura(spellName, unit, "PLAYER", "HELPFUL")
+ scanForAura(spellName, unit, "PLAYER", "HARMFUL")
+ end
+ else
+ -- No filter provided: scan buffs then debuffs
+ scanForAura(spellName, unit, nil, "HELPFUL")
+ scanForAura(spellName, unit, nil, "HARMFUL")
+ end
+
+ -- Last resort: generic GetAuraDataByIndex scan
+ for i = 1, 40 do
+ local aura = api.GetAuraDataByIndex(unit, i, filter)
+ if not aura then break end
+ if aura.name == spellName then return aura end
+ end
+
+ return nil
+end
+
+-- Tooltip Hook Compatibility
+-- TBC: Use OnTooltipSetSpell script hook
+api.HookTooltipSetSpell = function(callback)
+ if GameTooltip.HookScript then
+ GameTooltip:HookScript("OnTooltipSetSpell", function(self)
+ local _, id = self:GetSpell()
+ if id then
+ callback(self, id)
+ end
+ end)
+ end
+end
+
+-- TBC: Use OnTooltipSetUnit script hook
+api.HookTooltipSetUnit = function(callback)
+ if GameTooltip.HookScript then
+ GameTooltip:HookScript("OnTooltipSetUnit", function(self)
+ callback(self)
+ end)
+ end
+end
+
+-- TBC: Use OnTooltipSetItem script hook
+api.HookTooltipSetItem = function(tooltip, callback)
+ if tooltip and tooltip.HookScript then
+ tooltip:HookScript("OnTooltipSetItem", function(self)
+ callback(self)
+ end)
+ end
+end
+
+-- UnitInPhase wrapper
+-- TBC: Use the global UnitInPhase function if available
+api.UnitInPhase = function(unit)
+ if _G.UnitInPhase then
+ return _G.UnitInPhase(unit)
+ end
+ -- Fallback: assume in phase if unit exists
+ if _G.UnitExists then
+ return _G.UnitExists(unit)
+ end
+ return true
+end
+
+-- GetNumQuestLogEntries wrapper
+-- TBC: Use the global GetNumQuestLogEntries function
+api.GetNumQuestLogEntries = function()
+ if _G.GetNumQuestLogEntries then
+ return _G.GetNumQuestLogEntries()
+ end
+ return 0
+end
+
+-- GetQuestLogTitle wrapper
+-- TBC: Use the global GetQuestLogTitle function
+api.GetQuestLogTitle = function(questLogIndex)
+ if _G.GetQuestLogTitle then
+ return _G.GetQuestLogTitle(questLogIndex)
+ end
+ return nil
+end
+
+-- Talent System - TBC uses tier/column system
+api.getTalentInfo = function(spec, spellTalents)
+ local talents = {}
+
+ -- -- Initial profiles (spec > 1400) don't have talents
+ -- if spec > 1400 then return talents end
+
+ -- local activeSpecGroup = _G.C_SpecializationInfo and _G.C_SpecializationInfo.GetActiveSpecGroup and _G.C_SpecializationInfo.GetActiveSpecGroup()
+ -- if not activeSpecGroup then return talents end
+
+ -- -- TBC talents: 7 tiers x 3 columns
+ -- for tier = 1, 7 do
+ -- for column = 1, 3 do
+ -- local info = _G.C_SpecializationInfo and _G.C_SpecializationInfo.GetTalentInfo and
+ -- _G.C_SpecializationInfo.GetTalentInfo({ tier = tier, column = column, specializationIndex = activeSpecGroup })
+ -- if info and info.spellID then
+ -- talents[info.spellID] = {
+ -- active = info.selected and true or false,
+ -- rank = 0, -- TBC talents don't expose rank
+ -- tier = tier,
+ -- column = column,
+ -- }
+ -- end
+ -- end
+ -- end
+
+ return talents
+end
+
+-- GCD Spell List for TBC
+-- In TBC, spell 61304 doesn't exist, so we need class-specific spells to check GCD
+local GCDSpells = {
+ DRUID = {
+ NONE = 5176, -- Wrath
+ CAT = 5221 -- Shred
+ },
+ HUNTER = 1978, -- Serpent Sting
+ MAGE = 133, -- Fireball
+ PALADIN = 635, -- Holy Light
+ PRIEST = 2050, -- Holy Smite (Lesser Heal)
+ ROGUE = 1752, -- Sinister Strike
+ SHAMAN = 403, -- Lightning Bolt
+ WARLOCK = 686, -- Shadow Bolt
+ WARRIOR = 772 -- Rend
+}
+
+-- GetGCDSpellID - Returns the appropriate spell ID to check GCD for TBC
+-- This replaces the Retail spell 61304 which doesn't exist in TBC
+api.GetGCDSpellID = function()
+ local _, class = _G.UnitClass("player")
+ if not class then return 5176 end -- Default to Wrath if class unknown
+
+ local gcdSpell = GCDSpells[class]
+
+ -- Handle Druid form-specific GCD spells
+ if class == "DRUID" and type(gcdSpell) == "table" then
+ local form = _G.GetShapeshiftForm and _G.GetShapeshiftForm()
+ if form == 1 then -- Cat Form
+ return gcdSpell.CAT
+ else
+ return gcdSpell.NONE
+ end
+ end
+
+ return gcdSpell or 5176 -- Return class spell or default
+end
+
+--FindBaseSpellByID wrapper
+-- TBC: Use the global FindBaseSpellByID function if available
+api.FindBaseSpellByID = function(spellID)
+ if C_SpellBook and C_SpellBook.FindBaseSpellByID then
+ return C_SpellBook.FindBaseSpellByID(spellID)
+ end
+ if _G.FindBaseSpellByID then
+ if spellID == nil or api.GetSpellInfo(spellID) == nil then
+ -- print("Invalid Spell ID: " .. tostring(spellID))
+ end
+ return _G.FindBaseSpellByID(spellID)
+ end
+ return spellID
+end
+
+-- Print version info on load
+if br.api.compat.getVersionInfo then
+ local versionInfo = br.api.compat:getVersionInfo()
+ print("|cffA330C9BadRotations|r |cffFFFFFFAPI:|r TBC compatibility layer loaded (Build: " .. versionInfo.build .. ")")
+end
diff --git a/System/Functions/Action.lua b/System/Functions/Action.lua
index 5b0c6be232..afabd8d3fa 100644
--- a/System/Functions/Action.lua
+++ b/System/Functions/Action.lua
@@ -1,38 +1,43 @@
local _, br = ...
-function br.canFly()
+br.functions.action = br.functions.action or {}
+local action = br.functions.action
+local aura = br.functions.aura
+local misc = br.functions.misc
+
+function action:canFly()
return br._G.IsOutdoors() and br._G.IsFlyableArea()
end
--- if canHeal("target") then
-function br.canHeal(Unit)
+-- if br.functions.action:canHeal("target") then
+function action:canHeal(Unit)
if
- br.GetUnitExists(Unit) and br._G.UnitInRange(Unit) == true and br._G.UnitCanCooperate("player", Unit) and not br._G.UnitIsEnemy("player", Unit) and not br._G.UnitIsCharmed(Unit) and
- not br.GetUnitIsDeadOrGhost(Unit) and
- br.getLineOfSight(Unit) == true and
- not br.UnitDebuffID(Unit, 33786)
+ br.functions.unit:GetUnitExists(Unit) and br._G.UnitInRange(Unit) == true and br._G.UnitCanCooperate("player", Unit) and not br._G.UnitIsEnemy("player", Unit) and not br._G.UnitIsCharmed(Unit) and
+ not br.functions.unit:GetUnitIsDeadOrGhost(Unit) and
+ misc:getLineOfSight(Unit) == true and
+ not aura:UnitDebuffID(Unit, 33786)
then
return true
end
return false
end
--- if br.canRun() then
-function br.canRun()
- if br.getOptionCheck("Pause") ~= 1 then
- if br.isAlive("player") then
+-- if br.functions.action:canRun() then
+function action:canRun()
+ if misc:getOptionCheck("Pause") ~= 1 then
+ if br.functions.unit:isAlive("player") then
if
br._G.SpellIsTargeting() or --or UnitInVehicle("Player")
- (br._G.IsMounted() and not br.UnitBuffID("player", 164222) and not br.UnitBuffID("player", 165803) and not br.UnitBuffID("player", 157059) and not br.UnitBuffID("player", 157060)) or
- br.UnitBuffID("player", 11392) ~= nil or
- br.UnitBuffID("player", 80169) ~= nil or
- br.UnitBuffID("player", 87959) ~= nil or
- br.UnitBuffID("player", 104934) ~= nil or
- br.UnitBuffID("player", 9265) ~= nil
+ (br._G.IsMounted() and not aura:UnitBuffID("player", 164222) and not aura:UnitBuffID("player", 165803) and not aura:UnitBuffID("player", 157059) and not aura:UnitBuffID("player", 157060)) or
+ aura:UnitBuffID("player", 11392) ~= nil or
+ aura:UnitBuffID("player", 80169) ~= nil or
+ aura:UnitBuffID("player", 87959) ~= nil or
+ aura:UnitBuffID("player", 104934) ~= nil or
+ aura:UnitBuffID("player", 9265) ~= nil
then -- Deep Sleep(SM)
return nil
else
- if br.GetObjectExists("target") then
- if br.GetObjectID("target") ~= 5687 then
+ if br.functions.unit:GetObjectExists("target") then
+ if br.functions.unit:GetObjectID("target") ~= 5687 then
return nil
end
end
@@ -40,7 +45,7 @@ function br.canRun()
end
end
else
- br.ChatOverlay("|cffFF0000-BadRotations Paused-")
+ br.ui.chatOverlay:Show("|cffFF0000-BadRotations Paused-")
return false
end
end
diff --git a/System/Functions/Aura.lua b/System/Functions/Aura.lua
index 3fa74c9369..61ff87021b 100644
--- a/System/Functions/Aura.lua
+++ b/System/Functions/Aura.lua
@@ -1,24 +1,31 @@
local _, br = ...
-function br.AuraData(unit, index, filter)
- local auraData = br._G.C_UnitAuras.GetAuraDataByIndex(unit, index, filter)
+br.functions.aura = br.functions.aura or {}
+local aura = br.functions.aura
+
+function aura:AuraData(unit, index, filter)
+ local auraData = br.api.wow.GetAuraDataByIndex(unit, index, filter)
if not auraData then return nil end
- return AuraUtil.UnpackAuraData(auraData)
+ if _G.AuraUtil and _G.AuraUtil.UnpackAuraData then
+ return _G.AuraUtil.UnpackAuraData(auraData)
+ end
+ -- If AuraUtil doesn't exist, return the auraData structure
+ return auraData
end
-- Overwrite UnitBuff
-function br.UnitBuff(unit, index, filter)
- return br.AuraData(unit, index, filter)
+function aura:UnitBuff(unit, index, filter)
+ return aura:AuraData(unit, index, filter)
end
-- Overwrite UnitDebuff
-function br.UnitDebuff(unit, index, filter)
- return br.AuraData(unit, index, filter)
+function aura:UnitDebuff(unit, index, filter)
+ return aura:AuraData(unit, index, filter)
end
-function br.CancelUnitBuffID(unit, spellID, filter)
+function aura:CancelUnitBuffID(unit, spellID, filter)
-- local spellName = GetSpellInfo(spellID)
for i = 1, 40 do
- local _, _, _, _, _, _, _, _, _, buffSpellID = br.UnitBuff(unit, i)
+ local _, _, _, _, _, _, _, _, _, buffSpellID = aura:UnitBuff(unit, i)
if buffSpellID ~= nil then
if buffSpellID == spellID then
br._G.CancelUnitBuff(unit, i, filter)
@@ -30,9 +37,9 @@ function br.CancelUnitBuffID(unit, spellID, filter)
end
end
-function br.UnitAuraID(unit, spellID, filter)
- local buff = br.UnitBuffID(unit, spellID, filter)
- local debuff = br.UnitDebuffID(unit, spellID, filter)
+function aura:UnitAuraID(unit, spellID, filter)
+ local buff = aura:UnitBuffID(unit, spellID, filter)
+ local debuff = aura:UnitDebuffID(unit, spellID, filter)
if buff then
return buff
elseif debuff then
@@ -42,108 +49,168 @@ function br.UnitAuraID(unit, spellID, filter)
end
end
-function br.UnitBuffID(unit, spellID, filter)
- local spellName = br._G.GetSpellInfo(spellID)
+function aura:UnitBuffID(unit, spellID, filter)
+ local spellName = br.api.wow.GetSpellInfo(spellID)
local exactSearch = filter ~= nil and br._G.strfind(br._G.strupper(filter), "EXACT")
if unit == "player" then
- local auraInfo = C_UnitAuras.GetPlayerAuraBySpellID(spellID)
+ local auraInfo = br.api.wow.GetPlayerAuraBySpellID(spellID)
if auraInfo and auraInfo.expirationTime > br._G.GetTime() then return auraInfo end
end
if exactSearch then
for i = 1, 40 do
- local buffName, _, _, _, _, _, _, _, _, buffSpellID = br.UnitBuff(unit, i, "player")
+ local buffName, _, _, _, _, _, _, _, _, buffSpellID = aura:UnitBuff(unit, i, "player")
+ -- print("Unit: " .. tostring(unit) .. ", Spell: " .. tostring(spellName) .. ", ID: " .. tostring(spellID) .. ", Buff: " .. tostring(buffName) .. ", BuffID: " .. tostring(buffSpellID))
if buffName == nil then return nil end
if buffSpellID == spellID then
- return br.UnitBuff(unit, i)
+ return aura:UnitBuff(unit, i)
end
end
else
if filter ~= nil and br._G.strfind(br._G.strupper(filter), "PLAYER") then
-- br._G.print("Unit: " .. tostring(unit) .. ", Spell: " .. tostring(spellName) .. ", ID: " .. tostring(spellID))
- return br._G.AuraUtil.FindAuraByName(spellName, unit, "HELPFUL|PLAYER")
+ return br.api.wow.FindAuraByName(spellName, unit, "HELPFUL|PLAYER")
end
- return br._G.AuraUtil.FindAuraByName(spellName, unit, "HELPFUL")
+ return br.api.wow.FindAuraByName(spellName, unit, "HELPFUL")
end
end
-function br.UnitDebuffID(unit, spellID, filter)
- local thisUnit = br._G["ObjectPointer"](unit)
- local spellName = br._G.GetSpellInfo(spellID)
- -- Check Cache
- if br.isChecked("Cache Debuffs") then
- if br.enemy[thisUnit] ~= nil then
- if filter == nil then filter = "player" else filter = br._G["ObjectPointer"](filter) end
- if br.enemy[thisUnit].debuffs[filter] ~= nil then
- if br.enemy[thisUnit].debuffs[filter][spellID] ~= nil then
- return br.enemy[thisUnit].debuffs[filter][spellID](spellID, thisUnit)
- else
- return nil
- end
- end
- end
- end
+-- function aura:UnitDebuffID(unit, spellID, filter)
+-- local thisUnit = br._G["ObjectPointer"](unit)
+-- local spellName = br.api.wow.GetSpellInfo(spellID)
+-- -- Check Cache
+-- if br.functions.misc:isChecked("Cache Debuffs") then
+-- if br.engines.enemiesEngine.enemy[thisUnit] ~= nil then
+-- if filter == nil then filter = "player" else filter = br._G["ObjectPointer"](filter) end
+-- if br.engines.enemiesEngine.enemy[thisUnit].debuffs[filter] ~= nil then
+-- if br.engines.enemiesEngine.enemy[thisUnit].debuffs[filter][spellID] ~= nil then
+-- return br.engines.enemiesEngine.enemy[thisUnit].debuffs[filter][spellID](spellID, thisUnit)
+-- else
+-- return nil
+-- end
+-- end
+-- end
+-- end
- -- Failsafe if not cached
- if unit == "player" then
- local auraInfo = C_UnitAuras.GetPlayerAuraBySpellID(spellID)
- if auraInfo and auraInfo.expirationTime > br._G.GetTime() then return auraInfo end
- end
- local exactSearch = filter ~= nil and br._G.strfind(br._G.strupper(filter), "EXACT")
- if exactSearch then
- for i = 1, 40 do
- -- local buffName, _, _, _, _, _, _, _, _, buffSpellID = br._G.UnitDebuff(unit, i, "player")
- local auraInfo = C_UnitAuras.GetDebuffDataByIndex(unit, i, "PLAYER")
- if auraInfo == nil then return nil end
- if auraInfo.spellId == spellID then
- return auraInfo --br._G.UnitDebuff(unit, i, "player")
- end
- end
- else
- if filter ~= nil and br._G.strfind(br._G.strupper(filter), "PLAYER") then
- for i = 1, 40 do
- local auraInfo = C_UnitAuras.GetDebuffDataByIndex(unit, i, "HARMFUL|PLAYER")
- if auraInfo and auraInfo.name == spellName then return auraInfo end
- end
- -- return br._G.AuraUtil.FindAuraByName(spellName, unit, "HARMFUL|PLAYER")
- end
- for i = 1, 40 do
- local auraInfo = C_UnitAuras.GetDebuffDataByIndex(unit, i, "HARMFUL")
- if auraInfo and auraInfo.name == spellName then return auraInfo end
- end
- -- return br._G.AuraUtil.FindAuraByName(spellName, unit, "HARMFUL")
- end
+-- -- Failsafe if not cached
+-- if unit == "player" then
+-- local auraInfo = C_UnitAuras.GetPlayerAuraBySpellID(spellID)
+-- if auraInfo and auraInfo.expirationTime > br._G.GetTime() then return auraInfo end
+-- end
+-- local exactSearch = filter ~= nil and br._G.strfind(br._G.strupper(filter), "EXACT")
+-- if exactSearch then
+-- for i = 1, 40 do
+-- -- local buffName, _, _, _, _, _, _, _, _, buffSpellID = br._G.UnitDebuff(unit, i, "player")
+-- local auraInfo = C_UnitAuras.GetDebuffDataByIndex(unit, i, "PLAYER")
+-- if auraInfo and auraInfo.spellId == spellID then
+-- return auraInfo --br._G.UnitDebuff(unit, i, "player")
+-- end
+-- end
+-- else
+-- if filter ~= nil and br._G.strfind(br._G.strupper(filter), "PLAYER") then
+-- for i = 1, 40 do
+-- local auraInfo = C_UnitAuras.GetDebuffDataByIndex(unit, i, "HARMFUL|PLAYER")
+-- if auraInfo and auraInfo.name == spellName then return auraInfo end
+-- end
+-- -- return br._G.AuraUtil.FindAuraByName(spellName, unit, "HARMFUL|PLAYER")
+-- end
+-- for i = 1, 40 do
+-- local auraInfo = C_UnitAuras.GetDebuffDataByIndex(unit, i, "HARMFUL")
+-- if auraInfo and auraInfo.name == spellName then return auraInfo end
+-- end
+-- -- return br._G.AuraUtil.FindAuraByName(spellName, unit, "HARMFUL")
+-- end
+-- end
+local _UnitDebuffID_lock = false
+function aura:UnitDebuffID(unit, spellID, filter)
+ -- guard invalid input quickly
+ if not unit or not spellID then return nil end
+
+ -- prevent re-entrancy / infinite recursion that can cause "script ran too long"
+ if _UnitDebuffID_lock then return nil end
+ _UnitDebuffID_lock = true
+
+ local ok, r1, r2, r3, r4, r5 = pcall(function()
+ local thisUnit = br._G["ObjectPointer"](unit)
+ local spellName = br.api.wow.GetSpellInfo(spellID)
+ -- Check Cache
+ if br.functions.misc:isChecked("Cache Debuffs") then
+ if thisUnit and br.engines.enemiesEngine.enemy[thisUnit] ~= nil then
+ if filter == nil then filter = "player" else filter = br._G["ObjectPointer"](filter) end
+ if br.engines.enemiesEngine.enemy[thisUnit].debuffs[filter] ~= nil then
+ if br.engines.enemiesEngine.enemy[thisUnit].debuffs[filter][spellID] ~= nil then
+ return br.engines.enemiesEngine.enemy[thisUnit].debuffs[filter][spellID](spellID, thisUnit)
+ else
+ return nil
+ end
+ end
+ end
+ end
+
+ -- Failsafe if not cached
+ if unit == "player" then
+ local auraInfo = br.api.wow.GetPlayerAuraBySpellID(spellID)
+ if auraInfo and auraInfo.expirationTime > br._G.GetTime() then return auraInfo end
+ end
+ local exactSearch = filter ~= nil and br._G.strfind(br._G.strupper(filter), "EXACT")
+ if exactSearch then
+ for i = 1, 40 do
+ local auraInfo = br.api.wow.GetDebuffDataByIndex(unit, i, "PLAYER")
+ if auraInfo and auraInfo.spellId == spellID then
+ return auraInfo
+ end
+ end
+ else
+ if filter ~= nil and br._G.strfind(br._G.strupper(filter), "PLAYER") then
+ for i = 1, 40 do
+ local auraInfo = br.api.wow.GetDebuffDataByIndex(unit, i, "HARMFUL|PLAYER")
+ if auraInfo and auraInfo.name == spellName then return auraInfo end
+ end
+ return nil -- Don't fall through to non-player filter if PLAYER was specified
+ end
+ for i = 1, 40 do
+ local auraInfo = br.api.wow.GetDebuffDataByIndex(unit, i, "HARMFUL")
+ if auraInfo and auraInfo.name == spellName then return auraInfo end
+ end
+ end
+ return nil
+ end)
+
+ -- release lock and return results (if pcall failed, return nil)
+ _UnitDebuffID_lock = false
+ if not ok then return nil end
+ return r1, r2, r3, r4, r5
end
local function Dispel(unit, stacks, buffDuration, buffRemain, buffSpellID, buff)
if not buff then
- if buffSpellID == 288388 then
- if stacks >= br.getOptionValue("Reaping") or not br._G.UnitAffectingCombat("player") then
+ if buffSpellID == (br.functions.spell:getHighestSpellID("reaping") or 288388) then
+ if stacks >= br.functions.misc:getOptionValue("Reaping") or not br._G.UnitAffectingCombat("player") then
return true
else
return false
end
- elseif buffSpellID == 282566 then
- if stacks >= br.getOptionValue("Promise of Power") then
+ elseif buffSpellID == (br.functions.spell:getHighestSpellID("promiseOfPower") or 282566) then
+ if stacks >= br.functions.misc:getOptionValue("Promise of Power") then
return true
else
return false
end
- elseif buffSpellID == 303657 and br.isChecked("Arcane Burst")
- and buffDuration - buffRemain > (br.getValue("Dispel delay") - 0.3 + math.random() * 0.6)
+ elseif buffSpellID == (br.functions.spell:getHighestSpellID("arcaneBurst") or 303657) and br.functions.misc:isChecked("Arcane Burst")
+ and buffDuration - buffRemain > (br.functions.misc:getValue("Dispel delay") - 0.3 + math.random() * 0.6)
then
return true
- elseif br.novaEngineTables.DispelID[buffSpellID] ~= nil then
- if (stacks >= br.novaEngineTables.DispelID[buffSpellID].stacks or br.isChecked("Ignore Stack Count"))
+ elseif br.engines.healingEngineCollections.DispelID[buffSpellID] ~= nil then
+ if (stacks >= br.engines.healingEngineCollections.DispelID[buffSpellID].stacks or br.functions.misc:isChecked("Ignore Stack Count"))
then
- if br.novaEngineTables.DispelID[buffSpellID].stacks ~= 0
- and br.novaEngineTables.DispelID[buffSpellID].range == nil
+ if br.engines.healingEngineCollections.DispelID[buffSpellID].stacks ~= 0
+ and br.engines.healingEngineCollections.DispelID[buffSpellID].range == nil
then
return true
else
- if buffDuration - buffRemain > (br.getValue("Dispel delay") - 0.3 + math.random() * 0.6) then -- Dispel Delay then
- if br.novaEngineTables.DispelID[buffSpellID].range ~= nil then
- if not br.isChecked("Ignore Range Check")
- and #br.getAllies(unit, br.novaEngineTables.DispelID[buffSpellID].range) > 1
+ if buffDuration - buffRemain > (br.functions.misc:getValue("Dispel delay") - 0.3 + math.random() * 0.6) then -- Dispel Delay then
+ if br.engines.healingEngineCollections.DispelID[buffSpellID].range ~= nil then
+ if not br.functions.misc:isChecked("Ignore Range Check")
+ and #br.engines.healingEngineFunctions:getAllies(unit, br.engines.healingEngineCollections.DispelID[buffSpellID].range) > 1
then
return false
end
@@ -158,8 +225,8 @@ local function Dispel(unit, stacks, buffDuration, buffRemain, buffSpellID, buff)
end
return nil
elseif buff then
- if br.novaEngineTables.PurgeID[buffSpellID] ~= nil then
- if (buffDuration - buffRemain) > (br.getValue("Dispel delay") - 0.3 + math.random() * 0.6) then
+ if br.engines.healingEngineCollections.PurgeID[buffSpellID] ~= nil then
+ if (buffDuration - buffRemain) > (br.functions.misc:getValue("Dispel delay") - 0.3 + math.random() * 0.6) then
return true
end
return false
@@ -168,11 +235,16 @@ local function Dispel(unit, stacks, buffDuration, buffRemain, buffSpellID, buff)
end
end
--- if br.canDispel("target",SpellID) == true then
-function br.canDispel(Unit, spellID)
+-- if br.functions.aura:canDispel("target",SpellID) == true then
+function aura:canDispel(Unit, spellID)
+ -- Initialize dispel tracking table if it doesn't exist
+ if not br.dispelTracker then
+ br.dispelTracker = {}
+ end
+
-- first, check DoNotDispell list
- for i = 1, #br.novaEngineTables.DoNotDispellList do
- if br.novaEngineTables.DoNotDispellList[i].id == spellID then
+ for i = 1, #br.engines.healingEngineCollections.DoNotDispellList do
+ if br.engines.healingEngineCollections.DoNotDispellList[i].id == spellID then
return false
end
end
@@ -184,83 +256,86 @@ function br.canDispel(Unit, spellID)
end
if ClassNum == 2 then --Paladin
-- Cleanse (Holy)
- if spellID == 4987 then typesList = { "Poison", "Disease", "Magic" } end
+ if spellID == (br.functions.spell:getHighestSpellID("cleanse") or 4987) then typesList = { "Poison", "Disease", "Magic" } end
-- Cleanse Toxins (Ret, Prot)
- if spellID == 213644 then typesList = { "Poison", "Disease" } end
+ if spellID == (br.functions.spell:getHighestSpellID("cleanseToxins") or 213644) then typesList = { "Poison", "Disease" } end
end
- if ClassNum == 3 then --Hunter
- if spellID == 19801 then typesList = { "Magic", "" } end --tranq shot
+ if ClassNum == 3 then --Hunter
+ if spellID == (br.functions.spell:getHighestSpellID("tranquilizingShot") or 19801) then typesList = { "Magic", "" } end --tranq shot
end
- if ClassNum == 4 then --Rogue
- if spellID == 31224 then typesList = { "Poison", "Curse", "Disease", "Magic" } end -- Cloak of Shadows
- if spellID == 5938 then typesList = { "" } end --shiv
+ if ClassNum == 4 then --Rogue
+ if spellID == (br.functions.spell:getHighestSpellID("cloakOfShadows") or 31224) then typesList = { "Poison", "Curse", "Disease", "Magic" } end -- Cloak of Shadows
+ if spellID == (br.functions.spell:getHighestSpellID("shiv") or 5938) then typesList = { "" } end --shiv
end
- if ClassNum == 5 then --Priest
+ if ClassNum == 5 then --Priest
-- Purify
- if spellID == 527 then typesList = { "Disease", "Magic" } end
+ if spellID == (br.functions.spell:getHighestSpellID("purify") or 527) then typesList = { "Disease", "Magic" } end
-- Mass Dispell
- if spellID == 32375 then typesList = { "Magic" } end
+ if spellID == (br.functions.spell:getHighestSpellID("massDispel") or 32375) then typesList = { "Magic" } end
-- Dispel Magic
- if spellID == 528 then typesList = { "Magic" } end
+ if spellID == (br.functions.spell:getHighestSpellID("dispelMagic") or 528) then typesList = { "Magic" } end
end
if ClassNum == 6 then --Death Knight
typesList = {}
end
if ClassNum == 7 then --Shaman
-- Cleanse Spirit
- if spellID == 51886 then typesList = { "Curse" } end
+ if spellID == (br.functions.spell:getHighestSpellID("cleanseSpirit") or 51886) then typesList = { "Curse" } end
-- Purify Spirit
- if spellID == 77130 then typesList = { "Curse", "Magic" } end
+ if spellID == (br.functions.spell:getHighestSpellID("purifySpirit") or 77130) then typesList = { "Curse", "Magic" } end
-- Purge
- if spellID == 370 then typesList = { "Magic" } end
+ if spellID == (br.functions.spell:getHighestSpellID("purge") or 370) then typesList = { "Magic" } end
end
if ClassNum == 8 then --Mage
-- Remove Curse
- if spellID == 475 then typesList = { "Curse" } end
+ if spellID == (br.functions.spell:getHighestSpellID("removeCurse") or 475) then typesList = { "Curse" } end
end
if ClassNum == 9 then --Warlock
- if spellID == 19505 then typesList = { "Magic" } end
+ if spellID == (br.functions.spell:getHighestSpellID("devourMagic") or 19505) then typesList = { "Magic" } end
end
if ClassNum == 10 then --Monk
-- Detox (MW)
--if GetSpecialization() == 2 then
- if spellID == 115450 then typesList = { "Poison", "Disease", "Magic" } end
- -- Detox (WW or BM)
- --else
- if spellID == 218164 then typesList = { "Poison", "Disease" } end
- --end
+ if spellID == (br.functions.spell:getHighestSpellID("detox") or 115450) or spellID == 218164 then typesList = { "Poison", "Disease" } end --, "Magic" } end
+ -- Detox (WW or BM) handled above via detox key or fallback 218164
-- Diffuse Magic
-- if spellID == 122783 then typesList = { "Magic" } end
end
if ClassNum == 11 then --Druid
+ -- Abolish Poison
+ if spellID == (br.functions.spell:getHighestSpellID("abolishPoison") or 2893) then typesList = { "Poison" } end
+ -- Cure Poison
+ if spellID == (br.functions.spell:getHighestSpellID("curePoison") or 8946) then typesList = { "Poison" } end
-- Remove Corruption
- if spellID == 2782 then typesList = { "Poison", "Curse" } end
+ if spellID == (br.functions.spell:getHighestSpellID("removeCorruption") or 2782) then typesList = { "Poison", "Curse" } end
+ -- Remove Curse
+ if spellID == 2783 then typesList = { "Curse" } end
-- Nature's Cure
- if spellID == 88423 then typesList = { "Poison", "Curse", "Magic" } end
+ if spellID == (br.functions.spell:getHighestSpellID("naturesCure") or 88423) then typesList = { "Poison", "Curse", "Magic" } end
-- Symbiosis: Cleanse
if spellID == 122288 then typesList = { "Poison", "Disease" } end
-- Soothe
- if spellID == 2908 then
+ if spellID == (br.functions.spell:getHighestSpellID("soothe") or 2908) then
typesList = { "" }
end
end
if ClassNum == 12 then --Demon Hunter
-- Consume Magic
- if spellID == 278326 then typesList = { "Magic" } end
+ if spellID == (br.functions.spell:getHighestSpellID("consumeMagic") or 278326) then typesList = { "Magic" } end
end
if ClassNum == 13 then -- Evoker
-- Expunge
- if spellID == 365585 then typesList = { "Poison" } end
+ if spellID == (br.functions.spell:getHighestSpellID("expunge") or 365585) then typesList = { "Poison" } end
-- Cauterizing Flame
- if spellID == 374251 then typesList = { "Bleed", "Poison", "Curse", "Disease" } end
+ if spellID == (br.functions.spell:getHighestSpellID("cauterizingFlame") or 374251) then typesList = { "Bleed", "Poison", "Curse", "Disease" } end
-- Naturalize
- if spellID == 360823 then typesList = { "Magic", "Poison" } end
+ if spellID == (br.functions.spell:getHighestSpellID("naturalize") or 360823) then typesList = { "Magic", "Poison" } end
end
if br.player.race == "BloodElf" then --Blood Elf
-- Arcane Torrent
- if spellID == select(7, br._G.GetSpellInfo(69179)) then typesList = { "Magic" } end
+ if spellID == select(7, br.api.wow.GetSpellInfo(br.lists.racials and br.lists.racials._bloodElfBase or 69179)) then typesList = { "Magic" } end
end
- if br.hasItem(177278) and spellID == 177278 then typesList = { "Disease", "Poison", "Curse", } end -- Phail of Serenity
+ if br.functions.item:hasItem(br.functions.item:getHighestItemID("phialOfSerenity") or 177278) and spellID == (br.functions.item:getHighestItemID("phialOfSerenity") or 177278) then typesList = { "Disease", "Poison", "Curse", } end -- Phail of Serenity
local function ValidType(debuffType)
local typeCheck = false
if typesList == nil then
@@ -276,53 +351,82 @@ function br.canDispel(Unit, spellID)
return typeCheck
end
local i = 1
- if not br._G.UnitPhaseReason(Unit) then
- if br.GetUnitIsFriend("player", Unit) then
- while br._G.UnitDebuff(Unit, i) do
- local _, _, stacks, debuffType, debuffDuration, debuffExpire, _, _, _, debuffid = br._G.UnitDebuff(Unit,
- i)
- local debuffRemain = debuffExpire - br._G.GetTime()
- if (debuffType and ValidType(debuffType)) then
- local delay = br.getValue("Dispel delay") - 0.3 + math.random() * 0.6
- if debuffid == 284663 and (br.GetHP(Unit) < br.getOptionValue("Bwonsamdi's Wrath HP")
- or (br._G.UnitGroupRolesAssigned(Unit) == "TANK" and (debuffDuration - debuffRemain) > delay)) then
+ if br._G.UnitInPhase(Unit) then
+ if br.functions.unit:GetUnitIsFriend("player", Unit) then
+ while br.api.wow.GetDebuffDataByIndex(Unit, i) do
+ local debuffInfo = br.api.wow.GetDebuffDataByIndex(Unit, i)
+ local debuffRemain = debuffInfo.expirationTime - br._G.GetTime()
+ if (debuffInfo.dispelName and ValidType(debuffInfo.dispelName)) then
+ -- Create a unique key for this specific debuff instance
+ local trackerKey = Unit .. "_" .. debuffInfo.spellId .. "_" .. debuffInfo.expirationTime
+
+ -- If this is a new debuff, calculate and store a random reaction delay
+ if not br.dispelTracker[trackerKey] then
+ -- Human-like reaction time: base delay +/- 0.3 seconds
+ local baseDelay = br.functions.misc:getValue("Dispel delay")
+ local randomDelay = baseDelay - 0.3 + math.random() * 0.6
+ br.dispelTracker[trackerKey] = randomDelay
+ end
+
+ local delay = br.dispelTracker[trackerKey]
+
+ if debuffInfo.spellId == (br.functions.spell:getHighestSpellID("bwonsamdiWrath") or 284663) and (br.GetHP(Unit) < br.functions.misc:getOptionValue("Bwonsamdi's Wrath HP")
+ or (br._G.UnitGroupRolesAssigned(Unit) == "TANK" and (debuffInfo.duration - debuffRemain) > delay)) then
HasValidDispel = true
break
end
local dispelUnitObj
- for j = 1, #br.friend do
- local thisUnit = br.friend[j].unit
+ for j = 1, #br.engines.healingEngine.friend do
+ local thisUnit = br.engines.healingEngine.friend[j].unit
if Unit == thisUnit then
- dispelUnitObj = Dispel(thisUnit, stacks, debuffDuration, debuffRemain, debuffid)
+ dispelUnitObj = Dispel(thisUnit, debuffInfo.applications, debuffInfo.duration, debuffRemain, debuffInfo.spellId)
if dispelUnitObj ~= nil then
break
end
end
end
- if dispelUnitObj == nil and not br.isChecked("Dispel Only Whitelist")
- and (debuffDuration - debuffRemain) > delay then
+ if dispelUnitObj == nil and not br.functions.misc:isChecked("Dispel Only Whitelist")
+ and (debuffInfo.duration - debuffRemain) > delay then
HasValidDispel = true
+ -- Clean up the tracker once we've detected it (optional: keeps memory usage down)
+ -- br.dispelTracker[trackerKey] = nil
break
elseif dispelUnitObj == true then
HasValidDispel = true
+ -- br.dispelTracker[trackerKey] = nil
break
end
end
i = i + 1
end
else
- while br.UnitBuff(Unit, i) do
- local _, _, stacks, buffType, buffDuration, buffExpire, _, _, _, buffid = br.UnitBuff(Unit, i)
- local buffRemain = buffExpire - br._G.GetTime()
- if (buffType and ValidType(buffType)) and not br._G.UnitIsPlayer(Unit) then
- local dispelUnitObj = Dispel(Unit, stacks, buffDuration, buffRemain, buffid, true)
- if dispelUnitObj == nil and not br.isChecked("Purge Only Whitelist") then
- if (buffDuration - buffRemain) > (br.getValue("Dispel delay") - 0.3 + math.random() * 0.6) then
+ while br.api.wow.GetBuffDataByIndex(Unit, i) do
+ local buffInfo = br.api.wow.GetBuffDataByIndex(Unit, i)
+ local buffRemain = buffInfo.expirationTime - br._G.GetTime()
+ if (buffInfo.dispelName and ValidType(buffInfo.dispelName)) and not br._G.UnitIsPlayer(Unit) then
+ -- Create a unique key for this specific buff instance
+ local trackerKey = Unit .. "_" .. buffInfo.spellId .. "_" .. buffInfo.expirationTime
+
+ -- If this is a new buff, calculate and store a random reaction delay
+ if not br.dispelTracker[trackerKey] then
+ -- Human-like reaction time: base delay +/- 0.3 seconds
+ local baseDelay = br.functions.misc:getValue("Dispel delay")
+ local randomDelay = baseDelay - 0.3 + math.random() * 0.6
+ br.dispelTracker[trackerKey] = randomDelay
+ end
+
+ local delay = br.dispelTracker[trackerKey]
+
+ local dispelUnitObj = Dispel(Unit, buffInfo.applications, buffInfo.duration, buffRemain, buffInfo.spellId, true)
+ if dispelUnitObj == nil and not br.functions.misc:isChecked("Purge Only Whitelist") then
+ if (buffInfo.duration - buffRemain) > delay then
HasValidDispel = true
+ -- br.dispelTracker[trackerKey] = nil
break
end
elseif dispelUnitObj == true then
HasValidDispel = true
+ -- br.dispelTracker[trackerKey] = nil
break
end
end
@@ -330,11 +434,19 @@ function br.canDispel(Unit, spellID)
end
end
end
+
+ -- Periodically clean up old tracker entries to prevent memory bloat
+ -- Run cleanup every 30 seconds
+ if not br.dispelTrackerLastCleanup or (br._G.GetTime() - br.dispelTrackerLastCleanup) > 30 then
+ br.dispelTracker = {}
+ br.dispelTrackerLastCleanup = br._G.GetTime()
+ end
+
return HasValidDispel
end
-function br.getAuraDuration(Unit, AuraID, Source)
- local auraInfo,_,_,_,duration = br.UnitAuraID(Unit, AuraID, Source)
+function aura:getAuraDuration(Unit, AuraID, Source)
+ local auraInfo,_,_,_,duration = aura:UnitAuraID(Unit, AuraID, Source)
if not auraInfo then return 0 end
if auraInfo.duration then
return auraInfo.duration * 1
@@ -343,8 +455,8 @@ function br.getAuraDuration(Unit, AuraID, Source)
end
end
-function br.getAuraRemain(Unit, AuraID, Source)
- local auraInfo,_,_,_,_,expires = br.UnitAuraID(Unit, AuraID, Source)
+function aura:getAuraRemain(Unit, AuraID, Source)
+ local auraInfo,_,_,_,_,expires = aura:UnitAuraID(Unit, AuraID, Source)
if not auraInfo then return 0 end
if auraInfo.expirationTime then
return auraInfo.expirationTime - br._G.GetTime()
@@ -353,8 +465,8 @@ function br.getAuraRemain(Unit, AuraID, Source)
end
end
-function br.getAuraStacks(Unit, AuraID, Source)
- local auraInfo,_,stacks = br.UnitAuraID(Unit, AuraID, Source)
+function aura:getAuraStacks(Unit, AuraID, Source)
+ local auraInfo,_,stacks = aura:UnitAuraID(Unit, AuraID, Source)
if not auraInfo then return 0 end
if auraInfo.applications then
return auraInfo.applications
@@ -363,9 +475,9 @@ function br.getAuraStacks(Unit, AuraID, Source)
end
end
--- if br.getDebuffDuration("target",12345) < 3 then
-function br.getDebuffDuration(Unit, DebuffID, Source)
- local auraInfo,_,_,_,duration = br.UnitDebuffID(Unit, DebuffID, Source)
+-- if br.functions.aura:getDebuffDuration("target",12345) < 3 then
+function aura:getDebuffDuration(Unit, DebuffID, Source)
+ local auraInfo,_,_,_,duration = aura:UnitDebuffID(Unit, DebuffID, Source)
if not auraInfo then return 0 end
if auraInfo.duration then
return auraInfo.duration * 1
@@ -374,9 +486,9 @@ function br.getDebuffDuration(Unit, DebuffID, Source)
end
end
--- if br.getDebuffRemain("target",12345) < 3 then
-function br.getDebuffRemain(Unit, DebuffID, Source)
- local auraInfo,_,_,_,_,expires = br.UnitDebuffID(Unit, DebuffID, Source)
+-- if br.functions.aura:getDebuffRemain("target",12345) < 3 then
+function aura:getDebuffRemain(Unit, DebuffID, Source)
+ local auraInfo,_,_,_,_,expires = aura:UnitDebuffID(Unit, DebuffID, Source)
if not auraInfo then return 0 end
if auraInfo.expirationTime then
return auraInfo.expirationTime - br._G.GetTime()
@@ -385,9 +497,9 @@ function br.getDebuffRemain(Unit, DebuffID, Source)
end
end
--- if br.getDebuffStacks("target",138756) > 0 then
-function br.getDebuffStacks(Unit, DebuffID, Source)
- local auraInfo,_,stacks = br.UnitDebuffID(Unit, DebuffID, Source)
+-- if br.functions.aura:getDebuffStacks("target",138756) > 0 then
+function aura:getDebuffStacks(Unit, DebuffID, Source)
+ local auraInfo,_,stacks = aura:UnitDebuffID(Unit, DebuffID, Source)
if not auraInfo then return 0 end
if auraInfo.applications then
return auraInfo.applications
@@ -396,14 +508,14 @@ function br.getDebuffStacks(Unit, DebuffID, Source)
end
end
-function br.getDebuffCount(spellID)
+function aura:getDebuffCount(spellID)
local counter = 0
- for k, _ in pairs(br.enemy) do
- local thisUnit = br.enemy[k].unit
+ for k, _ in pairs(br.engines.enemiesEngine.enemy) do
+ local thisUnit = br.engines.enemiesEngine.enemy[k].unit
-- check if unit is valid
- -- if br.GetObjectExists(thisUnit) then
+ -- if br.functions.unit:GetObjectExists(thisUnit) then
-- increase counter for each occurences
- if br.UnitDebuffID(thisUnit, spellID, "player") then
+ if aura:UnitDebuffID(thisUnit, spellID, "player") then
counter = counter + 1
end
-- end
@@ -411,14 +523,14 @@ function br.getDebuffCount(spellID)
return tonumber(counter)
end
-function br.getDebuffRemainCount(spellID, remain)
+function aura:getDebuffRemainCount(spellID, remain)
local counter = 0
- for k, _ in pairs(br.enemy) do
- local thisUnit = br.enemy[k].unit
+ for k, _ in pairs(br.engines.enemiesEngine.enemy) do
+ local thisUnit = br.engines.enemiesEngine.enemy[k].unit
-- check if unit is valid
- -- if br.GetObjectExists(thisUnit) then
+ -- if br.functions.unit:GetObjectExists(thisUnit) then
-- increase counter for each occurences
- if br.UnitDebuffID(thisUnit, spellID, "player") and br.getDebuffRemain(thisUnit, spellID, "player") >= remain then
+ if aura:UnitDebuffID(thisUnit, spellID, "player") and aura:getDebuffRemain(thisUnit, spellID, "player") >= remain then
counter = counter + 1
end
-- end
@@ -426,16 +538,16 @@ function br.getDebuffRemainCount(spellID, remain)
return tonumber(counter)
end
-function br.getDebuffMinMax(spell, range, debuffType, returnType, source)
+function aura:getDebuffMinMax(spell, range, debuffType, returnType, source)
local thisMin = 99
local thisMax = 0
local lowestUnit = "target"
local maxUnit = "target"
- for k, _ in pairs(br.enemy) do
- local thisUnit = br.enemy[k].unit
- local distance = br.getDistance(thisUnit, source)
+ for k, _ in pairs(br.engines.enemiesEngine.enemy) do
+ local thisUnit = br.engines.enemiesEngine.enemy[k].unit
+ local distance = br.functions.range:getDistance(thisUnit, source)
local thisDebuff = br.player.debuff[spell][debuffType](thisUnit)
- if br.getFacing("player", thisUnit) and distance <= range and thisDebuff >= 0
+ if br.functions.unit:getFacing("player", thisUnit) and distance <= range and thisDebuff >= 0
and ((returnType == "min" and thisDebuff < thisMin) or (returnType == "max" and thisDebuff > thisMax))
then
if returnType == "min" then
@@ -456,14 +568,14 @@ function br.getDebuffMinMax(spell, range, debuffType, returnType, source)
end
end
-function br.getDebuffMinMaxButForPetsThisTime(spell, range, debuffType, returnType)
+function aura:getDebuffMinMaxButForPetsThisTime(spell, range, debuffType, returnType)
local thisMin = 99
local lowestUnit = "target"
- for k, _ in pairs(br.enemy) do
- local thisUnit = br.enemy[k].unit
- local distance = br.getDistance(thisUnit, "pet")
+ for k, _ in pairs(br.engines.enemiesEngine.enemy) do
+ local thisUnit = br.engines.enemiesEngine.enemy[k].unit
+ local distance = br.functions.range:getDistance(thisUnit, "pet")
local thisDebuff = br.player.debuff[spell][debuffType](thisUnit)
- if br.getFacing("player", thisUnit) and distance <= range and thisDebuff >= 0 and thisDebuff < thisMin then
+ if br.functions.unit:getFacing("player", thisUnit) and distance <= range and thisDebuff >= 0 and thisDebuff < thisMin then
if returnType == "min" or returnType == nil then
lowestUnit = thisUnit
thisMin = thisDebuff
@@ -474,8 +586,8 @@ function br.getDebuffMinMaxButForPetsThisTime(spell, range, debuffType, returnTy
end
-- if getBuffDuration("target",12345) < 3 then
-function br.getBuffDuration(Unit, BuffID, Source)
- local auraInfo,_,_,_,duration = br.UnitBuffID(Unit, BuffID, Source)
+function aura:getBuffDuration(Unit, BuffID, Source)
+ local auraInfo,_,_,_,duration = aura:UnitBuffID(Unit, BuffID, Source)
if not auraInfo then return 0 end
if auraInfo.duration then
return auraInfo.duration * 1
@@ -484,9 +596,9 @@ function br.getBuffDuration(Unit, BuffID, Source)
end
end
--- if br.getBuffRemain("target",12345) < 3 then
-function br.getBuffRemain(Unit, BuffID, Source)
- local auraInfo,_,_,_,_,expires = br.UnitBuffID(Unit, BuffID, Source)
+-- if br.functions.aura:getBuffRemain("target",12345) < 3 then
+function aura:getBuffRemain(Unit, BuffID, Source)
+ local auraInfo,_,_,_,_,expires = aura:UnitBuffID(Unit, BuffID, Source)
if not auraInfo then return 0 end
if auraInfo.expirationTime then
return auraInfo.expirationTime - br._G.GetTime()
@@ -495,9 +607,9 @@ function br.getBuffRemain(Unit, BuffID, Source)
end
end
--- if br.getBuffStacks(138756) > 0 then
-function br.getBuffStacks(Unit, BuffID, Source)
- local auraInfo,_,stacks = br.UnitBuffID(Unit, BuffID, Source)
+-- if br.functions.aura:getBuffStacks(138756) > 0 then
+function aura:getBuffStacks(Unit, BuffID, Source)
+ local auraInfo,_,stacks = aura:UnitBuffID(Unit, BuffID, Source)
if not auraInfo then return 0 end
if auraInfo.applications then
return auraInfo.applications
@@ -506,14 +618,14 @@ function br.getBuffStacks(Unit, BuffID, Source)
end
end
-function br.getBuffCount(spellID)
+function aura:getBuffCount(spellID)
local counter = 0
- for i = 1, #br.friend do
- local thisUnit = br.friend[i].unit
+ for i = 1, #br.engines.healingEngine.friend do
+ local thisUnit = br.engines.healingEngine.friend[i].unit
-- check if unit is valid
- -- if br.GetObjectExists(thisUnit) then
+ -- if br.functions.unit:GetObjectExists(thisUnit) then
-- increase counter for each occurences
- if br.UnitBuffID(thisUnit, spellID, "player") then
+ if aura:UnitBuffID(thisUnit, spellID, "player") then
counter = counter + 1
end
-- end
@@ -521,8 +633,8 @@ function br.getBuffCount(spellID)
return tonumber(counter)
end
-function br.getBuffReact(Unit, BuffID, Source)
- local auraInfo, _, _, _, duration, expire = br.UnitBuffID(Unit, BuffID, Source)
+function aura:getBuffReact(Unit, BuffID, Source)
+ local auraInfo, _, _, _, duration, expire = aura:UnitBuffID(Unit, BuffID, Source)
if not auraInfo then return false end
if auraInfo.duration then
duration = auraInfo.duration
@@ -535,7 +647,7 @@ function br.getBuffReact(Unit, BuffID, Source)
end
-- if getDisease(30,true,min) < 2 then
-function br.getDisease(range, aoe, mod)
+function aura:getDisease(range, aoe, mod)
if mod == nil then
mod = "min"
end
@@ -547,16 +659,16 @@ function br.getDisease(range, aoe, mod)
end
range = tonumber(range)
mod = tostring(mod)
- local dynTar = br.dynamicTarget(range, true)
- local dynTarAoE = br.dynamicTarget(range, false)
- local dist = br.getDistance("player", dynTar)
- local distAoE = br.getDistance("player", dynTarAoE)
- local ff = br.getDebuffRemain(dynTar, 55095, "player") or 0
- local ffAoE = br.getDebuffRemain(dynTarAoE, 55095, "player") or 0
- local bp = br.getDebuffRemain(dynTar, 55078, "player") or 0
- local bpAoE = br.getDebuffRemain(dynTarAoE, 55078, "player") or 0
- local np = br.getDebuffRemain(dynTar, 155159, "player") or 0
- local npAoE = br.getDebuffRemain(dynTarAoE, 155159, "player") or 0
+ local dynTar = br.engines.enemiesEngineFunctions:dynamicTarget(range, true)
+ local dynTarAoE = br.engines.enemiesEngineFunctions:dynamicTarget(range, false)
+ local dist = br.functions.range:getDistance("player", dynTar)
+ local distAoE = br.functions.range:getDistance("player", dynTarAoE)
+ local ff = aura:getDebuffRemain(dynTar, 55095, "player") or 0
+ local ffAoE = aura:getDebuffRemain(dynTarAoE, 55095, "player") or 0
+ local bp = aura:getDebuffRemain(dynTar, 55078, "player") or 0
+ local bpAoE = aura:getDebuffRemain(dynTarAoE, 55078, "player") or 0
+ local np = aura:getDebuffRemain(dynTar, 155159, "player") or 0
+ local npAoE = aura:getDebuffRemain(dynTarAoE, 155159, "player") or 0
local diseases = { ff, bp }
local diseasesAoE = { ffAoE, bpAoE }
local minD = 99
@@ -564,7 +676,7 @@ function br.getDisease(range, aoe, mod)
if mod == "min" then
if aoe == false then
if dist < range then
- if br.getTalent(7, 1) then
+ if br.functions.misc:getTalent(7, 1) then
return np
else
for i = 1, #diseases do
@@ -579,7 +691,7 @@ function br.getDisease(range, aoe, mod)
end
elseif aoe == true then
if distAoE < range then
- if br.getTalent(7, 1) then
+ if br.functions.misc:getTalent(7, 1) then
return npAoE
else
for i = 1, #diseasesAoE do
@@ -596,7 +708,7 @@ function br.getDisease(range, aoe, mod)
elseif mod == "max" then
if aoe == false then
if dist < range then
- if br.getTalent(7, 1) then
+ if br.functions.misc:getTalent(7, 1) then
return np
else
for i = 1, #diseases do
@@ -611,7 +723,7 @@ function br.getDisease(range, aoe, mod)
end
elseif aoe == true then
if distAoE < range then
- if br.getTalent(7, 1) then
+ if br.functions.misc:getTalent(7, 1) then
return npAoE
else
for i = 1, #diseasesAoE do
@@ -629,25 +741,25 @@ function br.getDisease(range, aoe, mod)
end
-- TODO: update BL list
-function br.getLustID()
+function aura:getLustID()
for _, v in pairs(br.lists.spells.Shared.Shared.buffs["bloodLust"]) do
- if br.UnitBuffID("player", v) then return v end
+ if aura:UnitBuffID("player", v) then return v end
end
return 0
end
-function br.hasBloodLust()
- if br.UnitBuffID("player", 90355) or -- Ancient Hysteria
- br.UnitBuffID("player", 2825) or -- Bloodlust
- br.UnitBuffID("player", 146555) or -- Drums of Rage
- br.UnitBuffID("player", 390386) or -- Fury of the Aspects
- br.UnitBuffID("player", 32182) or -- Heroism
- br.UnitBuffID("player", 90355) or -- Netherwinds
- br.UnitBuffID("player", 80353) or -- Timewarp
- br.UnitBuffID("player", 230935) or -- Drums of the Mountain
- br.UnitBuffID("player", 256740) or -- Drums of the Maelstrom
- br.UnitBuffID("player", 441076) or -- Timeless Drums
- br.UnitBuffID("player", 264667) -- Primal Rage
+function aura:hasBloodLust()
+ if aura:UnitBuffID("player", 90355) or -- Ancient Hysteria
+ aura:UnitBuffID("player", 2825) or -- Bloodlust
+ aura:UnitBuffID("player", 146555) or -- Drums of Rage
+ aura:UnitBuffID("player", 390386) or -- Fury of the Aspects
+ aura:UnitBuffID("player", 32182) or -- Heroism
+ aura:UnitBuffID("player", 90355) or -- Netherwinds
+ aura:UnitBuffID("player", 80353) or -- Timewarp
+ aura:UnitBuffID("player", 230935) or -- Drums of the Mountain
+ aura:UnitBuffID("player", 256740) or -- Drums of the Maelstrom
+ aura:UnitBuffID("player", 441076) or -- Timeless Drums
+ aura:UnitBuffID("player", 264667) -- Primal Rage
then
return true
else
@@ -655,19 +767,19 @@ function br.hasBloodLust()
end
end
-function br.hasBloodLustRemain()
- if br.UnitBuffID("player", 90355) then
- return br.getBuffRemain("player", 90355)
- elseif br.UnitBuffID("player", 2825) then
- return br.getBuffRemain("player", 2825)
- elseif br.UnitBuffID("player", 146555) then
- return br.getBuffRemain("player", 146555)
- elseif br.UnitBuffID("player", 390386) then
- return br.getBuffRemain("player", 390386)
- elseif br.UnitBuffID("player", 32182) then
- return br.getBuffRemain("player", 32182)
- elseif br.UnitBuffID("player", 80353) then
- return br.getBuffRemain("player", 80353)
+function aura:hasBloodLustRemain()
+ if aura:UnitBuffID("player", 90355) then
+ return aura:getBuffRemain("player", 90355)
+ elseif aura:UnitBuffID("player", 2825) then
+ return aura:getBuffRemain("player", 2825)
+ elseif aura:UnitBuffID("player", 146555) then
+ return aura:getBuffRemain("player", 146555)
+ elseif aura:UnitBuffID("player", 390386) then
+ return aura:getBuffRemain("player", 390386)
+ elseif aura:UnitBuffID("player", 32182) then
+ return aura:getBuffRemain("player", 32182)
+ elseif aura:UnitBuffID("player", 80353) then
+ return aura:getBuffRemain("player", 80353)
else
return 0
end
diff --git a/System/Functions/Cast.lua b/System/Functions/Cast.lua
index 4b23743b8b..1a0d04efbc 100644
--- a/System/Functions/Cast.lua
+++ b/System/Functions/Cast.lua
@@ -1,32 +1,192 @@
local _, br = ...
+br.functions.cast = br.functions.cast or {}
+local cast = br.functions.cast
+local castIntentLocks = {} -- Tracks cast intent to prevent multiple cast.able() checks succeeding
+
+local function shouldPrintCastDebug()
+ return br.functions.misc and (br.functions.misc:isChecked("Cast Debug") or br.functions.misc:isChecked("Display Failcasts"))
+end
+
+local function setCastIntentLock(spellID, duration)
+ if spellID == nil then return end
+ if duration == nil then duration = 0.15 end
+ castIntentLocks[spellID] = br._G.GetTime() + duration
+end
+
+local function isCastIntentLocked(spellID)
+ if spellID == nil then return false end
+ return castIntentLocks[spellID] ~= nil and br._G.GetTime() < castIntentLocks[spellID]
+end
+
+local function getSameSpellCastRemain(spellID)
+ local now = br._G.GetTime()
+ local _, _, _, _, castEndTime, _, _, _, castingSpellID = br._G.UnitCastingInfo("player")
+ if castingSpellID == spellID and castEndTime and castEndTime > 0 then
+ return math.max(0, (castEndTime / 1000) - now)
+ end
+ local _, _, _, _, channelEndTime, _, _, channelingSpellID = br._G.UnitChannelInfo("player")
+ if channelingSpellID == spellID and channelEndTime and channelEndTime > 0 then
+ return math.max(0, (channelEndTime / 1000) - now)
+ end
+ return 0
+end
+
+local function takeCastStartSnapshot(spellID)
+ local spellCdBefore = 0
+ if br.functions.spell and br.functions.spell.getSpellCD then
+ spellCdBefore = br.functions.spell:getSpellCD(spellID) or 0
+ end
+ local currentSpellBefore = false
+ if br._G.IsCurrentSpell then
+ currentSpellBefore = br._G.IsCurrentSpell(spellID) == true
+ end
+ local buffBefore = false
+ if br.functions.aura and br.functions.aura.UnitBuffID then
+ buffBefore = br.functions.aura:UnitBuffID("player", spellID) ~= nil
+ end
+ return {
+ timeNow = br._G.GetTime(),
+ gcdBefore = br.functions.spell:getGlobalCD(),
+ castingBefore = br._G.UnitCastingInfo("player") ~= nil,
+ channelingBefore = br._G.UnitChannelInfo("player") ~= nil,
+ prevLastCastTime = (br.functions.lastCast and br.functions.lastCast.lastCastTable and br.functions.lastCast.lastCastTable.castTime and br.functions.lastCast.lastCastTable.castTime[spellID]) or 0,
+ spellCdBefore = spellCdBefore,
+ currentSpellBefore = currentSpellBefore,
+ buffBefore = buffBefore,
+ }
+end
+
+local function verifyCastStarted(spellID, snapshot, expectedSpellName)
+ expectedSpellName = expectedSpellName or br.api.wow.GetSpellInfo(spellID)
+ local gcdAfter = br.functions.spell:getGlobalCD()
+ local castNameAfter, _, _, _, _, _, _, _, castSpellIdAfter = br._G.UnitCastingInfo("player")
+ local channelNameAfter, _, _, _, _, _, _, _, channelSpellIdAfter = br._G.UnitChannelInfo("player")
+ local castingAfter = castNameAfter ~= nil
+ local channelingAfter = channelNameAfter ~= nil
+ local spellCdAfter = (br.functions.spell and br.functions.spell.getSpellCD) and (br.functions.spell:getSpellCD(spellID) or 0) or 0
+ local currentSpellAfter = br._G.IsCurrentSpell and (br._G.IsCurrentSpell(spellID) == true) or false
+ local buffAfter = (br.functions.aura and br.functions.aura.UnitBuffID) and (br.functions.aura:UnitBuffID("player", spellID) ~= nil) or false
+ local failedAt = (br.functions.lastCast and br.functions.lastCast.lastCastTable and br.functions.lastCast.lastCastTable.failedTime and br.functions.lastCast.lastCastTable.failedTime[spellID]) or 0
+ local recentlyFailed = failedAt ~= 0 and (br._G.GetTime() - failedAt) < 0.20
+
+ local startedNewCast = castingAfter and not snapshot.castingBefore
+ local startedNewChannel = channelingAfter and not snapshot.channelingBefore
+ local startedAnyNew = startedNewCast or startedNewChannel
+
+ local function matchesExpected(observedName, observedSpellId)
+ if observedSpellId ~= nil and observedSpellId ~= 0 then
+ return observedSpellId == spellID
+ end
+ if observedName ~= nil and expectedSpellName ~= nil then
+ return observedName == expectedSpellName
+ end
+ return true -- Can't determine; don't block.
+ end
+
+ local observedName = startedNewCast and castNameAfter or (startedNewChannel and channelNameAfter or nil)
+ local observedSpellId = startedNewCast and castSpellIdAfter or (startedNewChannel and channelSpellIdAfter or nil)
+ local spellMatches = (not startedAnyNew) or matchesExpected(observedName, observedSpellId)
+
+ local cooldownStarted = spellCdAfter > (snapshot.spellCdBefore or 0)
+ local buffGained = buffAfter and not (snapshot.buffBefore == true)
+ local currentSpellChangedOn = currentSpellAfter and not (snapshot.currentSpellBefore == true)
+ local currentSpellAlreadyOn = (spellID == 6603) and currentSpellAfter
+ local startedOffGcdEffect = cooldownStarted or buffGained or currentSpellChangedOn or currentSpellAlreadyOn
+
+ local castSucceeded = (gcdAfter > snapshot.gcdBefore) or startedAnyNew or startedOffGcdEffect
+ if startedAnyNew and not spellMatches then
+ castSucceeded = false
+ end
+
+ local newLastCastTime = (br.functions.lastCast and br.functions.lastCast.lastCastTable and br.functions.lastCast.lastCastTable.castTime and br.functions.lastCast.lastCastTable.castTime[spellID])
+ if not castSucceeded and newLastCastTime and newLastCastTime ~= 0 and newLastCastTime ~= snapshot.prevLastCastTime and (br._G.GetTime() - newLastCastTime) < 1 then
+ castSucceeded = true
+ end
+
+ local noObservableSignals = (gcdAfter == snapshot.gcdBefore)
+ and (not startedAnyNew)
+ and (not startedOffGcdEffect)
+ and ((newLastCastTime or 0) == (snapshot.prevLastCastTime or 0))
+ and (spellCdAfter == (snapshot.spellCdBefore or 0))
+ and (buffAfter == (snapshot.buffBefore == true))
+ and (currentSpellAfter == (snapshot.currentSpellBefore == true))
+
+ if not castSucceeded and recentlyFailed then
+ castSucceeded = false
+ end
+
+ local inconclusive = (not castSucceeded) and (not recentlyFailed) and noObservableSignals
+
+ return castSucceeded, {
+ gcdAfter = gcdAfter,
+ castingAfter = castingAfter,
+ channelingAfter = channelingAfter,
+ startedNewCast = startedNewCast,
+ startedNewChannel = startedNewChannel,
+ spellMatches = spellMatches,
+ castNameAfter = castNameAfter,
+ castSpellIdAfter = castSpellIdAfter,
+ channelNameAfter = channelNameAfter,
+ channelSpellIdAfter = channelSpellIdAfter,
+ newLastCastTime = newLastCastTime,
+ spellCdBefore = snapshot.spellCdBefore,
+ spellCdAfter = spellCdAfter,
+ currentSpellBefore = snapshot.currentSpellBefore,
+ currentSpellAfter = currentSpellAfter,
+ buffBefore = snapshot.buffBefore,
+ buffAfter = buffAfter,
+ cooldownStarted = cooldownStarted,
+ buffGained = buffGained,
+ currentSpellChangedOn = currentSpellChangedOn,
+ currentSpellAlreadyOn = currentSpellAlreadyOn,
+ recentlyFailed = recentlyFailed,
+ failedAt = failedAt,
+ inconclusive = inconclusive,
+ noObservableSignals = noObservableSignals,
+ }
+end
+
+local function printCastFailedDetails(prefix, spellID, spellName, snapshot, details)
+ br._G.print(prefix .. "Failed cast attempt: SpellID=" .. tostring(spellID) .. " Name=" .. tostring(spellName))
+ br._G.print(" GCD before/after: " .. tostring(snapshot.gcdBefore) .. " -> " .. tostring(details.gcdAfter))
+ br._G.print(" Casting before/after: " .. tostring(snapshot.castingBefore) .. " -> " .. tostring(details.castingAfter))
+ br._G.print(" Channeling before/after: " .. tostring(snapshot.channelingBefore) .. " -> " .. tostring(details.channelingAfter))
+ br._G.print(" PrevLastCastTime/newLastCastTime: " .. tostring(snapshot.prevLastCastTime) .. " -> " .. tostring(details.newLastCastTime))
+ br._G.print(" SpellCD before/after: " .. tostring(snapshot.spellCdBefore) .. " -> " .. tostring(details.spellCdAfter))
+ br._G.print(" IsCurrentSpell before/after: " .. tostring(snapshot.currentSpellBefore) .. " -> " .. tostring(details.currentSpellAfter))
+ br._G.print(" SelfBuff before/after: " .. tostring(snapshot.buffBefore) .. " -> " .. tostring(details.buffAfter))
+ br._G.print(" RecentlyFailed/failedAt: " .. tostring(details.recentlyFailed) .. " -> " .. tostring(details.failedAt))
+ br._G.print(" Inconclusive/noSignals: " .. tostring(details.inconclusive) .. " -> " .. tostring(details.noObservableSignals))
+end
+
-- if canCast(12345,true)
-function br.canCast(SpellID, KnownSkip, MovementCheck, thisUnit)
+function cast:canCast(SpellID, KnownSkip, MovementCheck, thisUnit)
if thisUnit == nil then thisUnit = "target" end
- local myCooldown = br.getSpellCD(SpellID) or 0
- -- local lagTolerance = br.getValue("Lag Tolerance") or 0
- if (KnownSkip == true or br.isKnown(SpellID)) and (br._G.UnitIsUnit(thisUnit, "target") and br._G.C_Spell.IsSpellUsable(SpellID) or true) and myCooldown < 0.1
- and (MovementCheck == false or myCooldown == 0 or br.isMoving("player") ~= true or br.UnitBuffID("player", 79206) ~= nil) then
+ local myCooldown = br.functions.spell:getSpellCD(SpellID) or 0
+ -- local lagTolerance = br.functions.misc:getValue("Lag Tolerance") or 0
+ if (KnownSkip == true or br.functions.spell:isKnown(SpellID)) and (br._G.UnitIsUnit(thisUnit, "target") and br._G.C_Spell.IsSpellUsable(SpellID) or true) and myCooldown < 0.1
+ and (MovementCheck == false or myCooldown == 0 or br.functions.misc:isMoving("player") ~= true or br.functions.aura:UnitBuffID("player", 79206) ~= nil) then
return true
end
end
-function br.castAoEHeal(spellID, numUnits, missingHP, rangeValue)
+function cast:castAoEHeal(spellID, numUnits, missingHP, rangeValue)
-- i start an iteration that i use to build each units Table,which i will reuse for the next second
if not br.holyRadianceRangeTable or not br.holyRadianceRangeTableTimer or br.holyRadianceRangeTableTimer <= br._G.GetTime() - 1 then
br.holyRadianceRangeTable = {}
- for i = 1, #br.friend do
+ for i = 1, #br.engines.healingEngine.friend do
-- i declare a sub-table for this unit if it dont exists
- if br.friend[i].distanceTable == nil then br.friend[i].distanceTable = {} end
+ if br.engines.healingEngine.friend[i].distanceTable == nil then br.engines.healingEngine.friend[i].distanceTable = {} end
-- i start a second iteration where i scan unit ranges from one another.
- for j = 1, #br.friend do
+ for j = 1, #br.engines.healingEngine.friend do
-- i make sure i dont compute unit range to hisself.
- if not br.GetUnitIsUnit(br.friend[i].unit, br.friend[j].unit) then
+ if not br.functions.unit:GetUnitIsUnit(br.engines.healingEngine.friend[i].unit, br.engines.healingEngine.friend[j].unit) then
-- table the units
- br.friend[i].distanceTable[j] = {
- distance = br.getDistance(br.friend[i].unit, br.friend[j].unit),
+ br.engines.healingEngine.friend[i].distanceTable[j] = {
+ distance = br.functions.range:getDistance(br.engines.healingEngine.friend[i].unit, br.engines.healingEngine.friend[j].unit),
unit =
- br.friend[j].unit,
- hp = br.friend[j].hp
+ br.engines.healingEngine.friend[j].unit,
+ hp = br.engines.healingEngine.friend[j].hp
}
end
end
@@ -36,16 +196,16 @@ function br.castAoEHeal(spellID, numUnits, missingHP, rangeValue)
local bestTarget, bestTargetUnits = 1, 1
-- now that nova range is built,i can iterate it
local inRange, missingHealth, mostMissingHealth = 0, 0, 0
- for i = 1, #br.friend do
- if br.friend[i].distanceTable ~= nil then
+ for i = 1, #br.engines.healingEngine.friend do
+ if br.engines.healingEngine.friend[i].distanceTable ~= nil then
-- i count units in range
- for j = 1, #br.friend do
- if br.friend[i].distanceTable[j] and br.friend[i].distanceTable[j].distance < rangeValue then
+ for j = 1, #br.engines.healingEngine.friend do
+ if br.engines.healingEngine.friend[i].distanceTable[j] and br.engines.healingEngine.friend[i].distanceTable[j].distance < rangeValue then
inRange = inRange + 1
- missingHealth = missingHealth + (100 - br.friend[i].distanceTable[j].hp)
+ missingHealth = missingHealth + (100 - br.engines.healingEngine.friend[i].distanceTable[j].hp)
end
end
- br.friend[i].inRangeForHolyRadiance = inRange
+ br.engines.healingEngine.friend[i].inRangeForHolyRadiance = inRange
-- i check if this is going to be the best unit for my spell
if missingHealth > mostMissingHealth then
bestTarget, bestTargetUnits, mostMissingHealth = i, inRange, missingHealth
@@ -53,31 +213,69 @@ function br.castAoEHeal(spellID, numUnits, missingHP, rangeValue)
end
end
if bestTargetUnits and bestTargetUnits > 3 and mostMissingHealth and missingHP and mostMissingHealth > missingHP then
- if br.castSpell(br.friend[bestTarget].unit, spellID, true, true) then return true end
+ if br.functions.cast:castSpell(br.engines.healingEngine.friend[bestTarget].unit, spellID, true, true) then return true end
end
end
+--cast spell on position x,y,z
+function cast:castAtPosition(X, Y, Z, SpellID)
+ local mouselookActive = false
+ if br._G.IsMouselooking() then
+ mouselookActive = true
+ br._G.MouselookStop()
+ end
+
+ -- Ground-targeted spells should not be cast "on" a unit token; doing so can force a self-cast.
+ -- We want the targeting cursor so we can click the provided world coordinates.
+ br._G.CastSpellByName(br.api.wow.GetSpellInfo(SpellID))
+
+ local baseZ = Z
+ local attempts = 0
+ local maxAttempts = 201
+
+ -- Try the provided Z first, then probe around it if needed.
+ while br._G["IsAoEPending"]() and attempts < maxAttempts do
+ local offset
+ if attempts == 0 then
+ offset = 0
+ else
+ local step = math.ceil(attempts / 2)
+ offset = (attempts % 2 == 1) and step or -step
+ end
+
+ br._G["ClickPosition"](X, Y, baseZ + offset)
+ attempts = attempts + 1
+ end
+
+ if mouselookActive then
+ br._G.MouselookStart()
+ end
+
+ if br._G["IsAoEPending"]() then return false end
+ return true
+end
+
-- castGround("target",12345,40)
-function br.castGround(Unit, SpellID, maxDistance, minDistance, radius, castTime)
+function cast:castGround(Unit, SpellID, maxDistance, minDistance, radius, castTime)
if radius == nil then radius = maxDistance end
if minDistance == nil then minDistance = 0 end
- local groundDistance = br.getDistance("player", Unit, "dist4") + 1
- local distance = br.getDistance("player", Unit)
+ local groundDistance = br.functions.range:getDistance("player", Unit, "dist4") + 1
+ local distance = br.functions.range:getDistance("player", Unit)
local mouselookActive = false
- if br.GetUnitExists(Unit) and br.getSpellCD(SpellID) == 0 and br.getLineOfSight("player", Unit)
+ if br.functions.unit:GetUnitExists(Unit) and br.functions.spell:getSpellCD(SpellID) == 0 and br.functions.misc:getLineOfSight("player", Unit)
and distance < maxDistance and distance >= minDistance
- and #br.getEnemies(Unit, radius) >= #br.getEnemies(Unit, radius, true)
+ and #br.engines.enemiesEngineFunctions:getEnemies(Unit, radius) >= #br.engines.enemiesEngineFunctions:getEnemies(Unit, radius, true)
then
if br._G.IsMouselooking() then
mouselookActive = true
br._G.MouselookStop()
end
- br._G.CastSpellByName(br._G.GetSpellInfo(SpellID))
+ br._G.CastSpellByName(br.api.wow.GetSpellInfo(SpellID))
local X, Y, Z
if castTime == nil or castTime == 0 then
- X, Y, Z = br.GetObjectPosition(Unit)
+ X, Y, Z = br.functions.unit:GetObjectPosition(Unit)
else
- X, Y, Z = br.GetFuturePostion(Unit, castTime)
+ X, Y, Z = br.functions.custom:GetFuturePostion(Unit, castTime)
end
--local distanceToGround = getGroundDistance(Unit) or 0
if groundDistance > maxDistance then
@@ -85,9 +283,6 @@ function br.castGround(Unit, SpellID, maxDistance, minDistance, radius, castTime
groundDistance - maxDistance)
end
br._G.ClickPosition((X + math.random() * 2), (Y + math.random() * 2), Z) --distanceToGround
- br.castPosition.x = X
- br.castPosition.y = Y
- br.castPosition.z = Z
if mouselookActive then
br._G.MouselookStart()
end
@@ -97,12 +292,12 @@ function br.castGround(Unit, SpellID, maxDistance, minDistance, radius, castTime
end
--castGroundLocation(123,456,98765,40,0,8)
-function br.castGroundLocation(X, Y, SpellID, maxDistance, minDistance, radius)
+function cast:castGroundLocation(X, Y, SpellID, maxDistance, minDistance, radius)
if X == nil or Y == nil then return false end
if radius == nil then radius = maxDistance end
if minDistance == nil then minDistance = 0 end
- --local groundDistance = br.getDistance("player",Unit,"dist4")+1
- local pX, pY, Z = br.GetObjectPosition("player")
+ --local groundDistance = br.functions.range:getDistance("player",Unit,"dist4")+1
+ local pX, pY, Z = br.functions.unit:GetObjectPosition("player")
local distance = br._G.sqrt(((X - pX) ^ 2) + ((Y - pY) ^ 2))
local mouselookActive = false
if distance < maxDistance and distance >= minDistance then
@@ -111,14 +306,11 @@ function br.castGroundLocation(X, Y, SpellID, maxDistance, minDistance, radius)
br._G.MouselookStop()
end
-- br._G.print("Casting Spell")
- br._G.CastSpellByName(br._G.GetSpellInfo(SpellID))
+ br._G.CastSpellByName(br.api.wow.GetSpellInfo(SpellID))
if br._G.IsAoEPending() then
-- br._G.print("Clicking Position")
br._G.ClickPosition((X + math.random() * 2), (Y + math.random() * 2), Z) --distanceToGround
end
- br.castPosition.x = X
- br.castPosition.y = Y
- br.castPosition.z = Z
if mouselookActive then
br._G.MouselookStart()
end
@@ -128,10 +320,10 @@ function br.castGroundLocation(X, Y, SpellID, maxDistance, minDistance, radius)
end
-- castGroundBetween("target",12345,40)
-function br.castGroundBetween(Unit, SpellID, maxDistance)
- if br.GetUnitExists(Unit) and br.getSpellCD(SpellID) <= 0.4 and br.getLineOfSight("player", Unit) and br.getDistance("player", Unit) <= maxDistance then
- br._G.CastSpellByName(br._G.GetSpellInfo(SpellID))
- local X, Y, Z = br.GetObjectPosition(Unit)
+function cast:castGroundBetween(Unit, SpellID, maxDistance)
+ if br.functions.unit:GetUnitExists(Unit) and br.functions.spell:getSpellCD(SpellID) <= 0.4 and br.functions.misc:getLineOfSight("player", Unit) and br.functions.range:getDistance("player", Unit) <= maxDistance then
+ br._G.CastSpellByName(br.api.wow.GetSpellInfo(SpellID))
+ local X, Y, Z = br.functions.unit:GetObjectPosition(Unit)
br._G.ClickPosition(X, Y, Z, true)
return true
end
@@ -139,7 +331,7 @@ function br.castGroundBetween(Unit, SpellID, maxDistance)
end
-- if shouldNotOverheal(spellCastTarget) > 80 then
-function br.shouldNotOverheal(Unit)
+function cast:shouldNotOverheal(Unit)
local myIncomingHeal, allIncomingHeal = 0, 0
if br._G.UnitGetIncomingHeals(Unit, "player") ~= nil then myIncomingHeal = br._G.UnitGetIncomingHeals(Unit, "player") end
if br._G.UnitGetIncomingHeals(Unit) ~= nil then allIncomingHeal = br._G.UnitGetIncomingHeals(Unit) end
@@ -151,9 +343,9 @@ function br.shouldNotOverheal(Unit)
overheal = allIncomingHeal
end
local CurShield = br._G.UnitHealth(Unit)
- if br.UnitDebuffID("player", 142861) then --Ancient Miasma
- CurShield = select(14, br.UnitDebuffID(Unit, 142863)) or select(14, br.UnitDebuffID(Unit, 142864)) or
- select(14, br.UnitDebuffID(Unit, 142865)) or (br._G.UnitHealthMax(Unit) / 2)
+ if br.functions.aura:UnitDebuffID("player", 142861) then --Ancient Miasma
+ CurShield = select(14, br.functions.aura:UnitDebuffID(Unit, 142863)) or select(14, br.functions.aura:UnitDebuffID(Unit, 142864)) or
+ select(14, br.functions.aura:UnitDebuffID(Unit, 142865)) or (br._G.UnitHealthMax(Unit) / 2)
overheal = 0
end
local overhealth = 100 * (CurShield + overheal) / br._G.UnitHealthMax(Unit)
@@ -165,14 +357,14 @@ function br.shouldNotOverheal(Unit)
end
-- if castHealGround(_HealingRain,18,80,3) then
-function br.castHealGround(SpellID, Radius, Health, NumberOfPlayers)
- if br.shouldStopCasting(SpellID) ~= true then
+function cast:castHealGround(SpellID, Radius, Health, NumberOfPlayers)
+ if br.engines.interrupts:shouldStopCasting(SpellID) ~= true then
local lowHPTargets, foundTargets = {}, {}
- for i = 1, #br.friend do
- if br.getHP(br.friend[i].unit) <= Health then
- if br.GetUnitIsVisible(br.friend[i].unit) and br.GetObjectExists(br.friend[i].unit) then
- local X, Y, Z = br.GetObjectPosition(br.friend[i].unit)
- br._G.tinsert(lowHPTargets, { unit = br.friend[i].unit, x = X, y = Y, z = Z })
+ for i = 1, #br.engines.healingEngine.friend do
+ if br.functions.unit:getHP(br.engines.healingEngine.friend[i].unit) <= Health then
+ if br.functions.unit:GetUnitIsVisible(br.engines.healingEngine.friend[i].unit) and br.functions.unit:GetObjectExists(br.engines.healingEngine.friend[i].unit) then
+ local X, Y, Z = br.functions.unit:GetObjectPosition(br.engines.healingEngine.friend[i].unit)
+ br._G.tinsert(lowHPTargets, { unit = br.engines.healingEngine.friend[i].unit, x = X, y = Y, z = Z })
end
end
end
@@ -225,16 +417,16 @@ function br.castHealGround(SpellID, Radius, Health, NumberOfPlayers)
medZ = medZ + foundTargets[i].z
end
medX, medY, medZ = medX / 3, medY / 3, medZ / 3
- local myX, myY = br.GetObjectPosition("player")
+ local myX, myY = br.functions.unit:GetObjectPosition("player")
if math.sqrt(((medX - myX) ^ 2) + ((medY - myY) ^ 2)) < 40 then
- br._G.CastSpellByName(br._G.GetSpellInfo(SpellID), "target")
+ br._G.CastSpellByName(br.api.wow.GetSpellInfo(SpellID), "target")
br._G.ClickPosition(medX, medY, medZ, true)
if SpellID == 145205 then br.shroomsTable[1] = { x = medX, y = medY, z = medZ } end
return true
end
elseif lowHPTargets ~= nil and #lowHPTargets == 1 and lowHPTargets[1].unit == "player" then
- local myX, myY, myZ = br.GetObjectPosition("player")
- br._G.CastSpellByName(br._G.GetSpellInfo(SpellID), "target")
+ local myX, myY, myZ = br.functions.unit:GetObjectPosition("player")
+ br._G.CastSpellByName(br.api.wow.GetSpellInfo(SpellID), "target")
br._G.ClickPosition(myX, myY, myZ, true)
if SpellID == 145205 then br.shroomsTable[1] = { x = medX, y = medY, z = medZ } end
return true
@@ -260,10 +452,10 @@ Tenth noCast True to return True/False instead of casting spell.
]]
-- castSpell("target",12345,true)
-- ( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 )
-function br.castSpell(Unit, SpellID, FacingCheck, MovementCheck, SpamAllowed, KnownSkip, DeadCheck, DistanceSkip,
+function cast:castSpell(Unit, SpellID, FacingCheck, MovementCheck, SpamAllowed, KnownSkip, DeadCheck, DistanceSkip,
usableSkip, noCast)
- if br.GetObjectExists(Unit) --and betterStopCasting(SpellID) ~= true
- and (not br.GetUnitIsDeadOrGhost(Unit) or DeadCheck)
+ if br.functions.unit:GetObjectExists(Unit) --and betterStopCasting(SpellID) ~= true
+ and (not br.functions.unit:GetUnitIsDeadOrGhost(Unit) or DeadCheck)
then
-- we create an usableSkip for some specific spells like hammer of wrath aoe mode
if usableSkip == nil then usableSkip = false end
@@ -274,75 +466,108 @@ function br.castSpell(Unit, SpellID, FacingCheck, MovementCheck, SpamAllowed, Kn
-- default noCast to false
if noCast == nil then noCast = false end
-- make sure it is a known spell
- if not (KnownSkip == true or br.isKnown(SpellID)) then return false end
+ if not (KnownSkip == true or br.functions.spell:isKnown(SpellID)) then return false end
-- gather our spell range information
- local spellRange = select(6, br._G.GetSpellInfo(SpellID))
+ local spellRange = select(6, br.api.wow.GetSpellInfo(SpellID))
if DistanceSkip == nil then DistanceSkip = false end
if spellRange == nil or (spellRange < 4 and DistanceSkip == false) then spellRange = 4 end
if DistanceSkip == true then spellRange = 40 end
-- Check unit,if it's player then we can skip facing
- if (Unit == nil or br.GetUnitIsUnit("player", Unit)) -- Player
- or (Unit ~= nil and br.GetUnitIsFriend("player", Unit)) -- Ally
+ if (Unit == nil or br.functions.unit:GetUnitIsUnit("player", Unit)) -- Player
+ or (Unit ~= nil and br.functions.unit:GetUnitIsFriend("player", Unit)) -- Ally
or br._G.IsHackEnabled("AlwaysFacing")
then
FacingCheck = true
- elseif br.isSafeToAttack(Unit) ~= true then -- enemy
+ elseif br.engines.enemiesEngineFunctions:isSafeToAttack(Unit) ~= true then -- enemy
return false
end
-- if MovementCheck is nil or false then we dont check it
- if MovementCheck == false or br.isMoving("player") ~= true
+ if MovementCheck == false or br.functions.misc:isMoving("player") ~= true
-- skip movement check during spiritwalkers grace and aspect of the fox
- or br.UnitBuffID("player", 79206) ~= nil
+ or br.functions.aura:UnitBuffID("player", 79206) ~= nil
then
-- if ability is ready and in range
- -- if br.getSpellCD(SpellID) < select(4,GetNetStats()) / 1000
- if (br.getSpellCD(SpellID) < select(4, br._G.GetNetStats()) / 1000) and (br.getOptionCheck("Skip Distance Check") or br.getDistance("player", Unit) <= spellRange or DistanceSkip == true or br.inRange(SpellID, Unit)) then
+ -- if br.functions.spell:getSpellCD(SpellID) < select(4,GetNetStats()) / 1000
+ if (br.functions.spell:getSpellCD(SpellID) < select(4, br._G.GetNetStats()) / 1000) and (br.functions.misc:getOptionCheck("Skip Distance Check") or br.functions.range:getDistance("player", Unit) <= spellRange or DistanceSkip == true or br.functions.range:inRange(SpellID, Unit)) then
-- if spam is not allowed
if SpamAllowed == false then
-- get our last/current cast
if br.timersTable == nil or (br.timersTable ~= nil and (br.timersTable[SpellID] == nil or br.timersTable[SpellID] <= br._G.GetTime() - 0.6)) then
- if (FacingCheck == true or br.getFacing("player", Unit) == true) and (br.GetUnitIsUnit("player", Unit) or br.units[Unit] ~= nil or br.getLineOfSight("player", Unit) == true) then
+ if (FacingCheck == true or br.functions.unit:getFacing("player", Unit) == true) and (br.functions.unit:GetUnitIsUnit("player", Unit) or br.engines.enemiesEngine.units[Unit] ~= nil or br.functions.misc:getLineOfSight("player", Unit) == true) then
if noCast then
return true
else
+ local snapshot = takeCastStartSnapshot(SpellID)
+
br.timersTable[SpellID] = br._G.GetTime()
-- currentTarget = UnitGUID(Unit) -- Not Used
br.botCast = true -- Used by old Queue Cast
br.botSpell = SpellID -- Used by old Queue Cast
- br._G.CastSpellByName(br._G.GetSpellInfo(SpellID), Unit)
+ br.botUnit = Unit
+ br._G.CastSpellByName(br.api.wow.GetSpellInfo(SpellID), Unit)
if br._G.IsAoEPending() then
local X, Y, Z = br._G.ObjectPosition(Unit)
br._G.ClickPosition(X, Y, Z)
end
- --lastSpellCast = SpellID
- -- change main button icon
- --if br.getOptionCheck("Start/Stop BadRotations") then
- br.mainButton:SetNormalTexture(select(3, br._G.GetSpellInfo(SpellID)))
- br.lastSpellCast = SpellID
- br.lastSpellTarget = br._G.UnitGUID(Unit)
- --end
- return true
+ local castSucceeded, details = verifyCastStarted(SpellID, snapshot)
+
+ if castSucceeded then
+ --lastSpellCast = SpellID
+ -- change main button icon
+ --if br.functions.misc:getOptionCheck("Start/Stop BadRotations") then
+ br.ui.toggles.mainButton:SetNormalTexture(select(3, br.api.wow.GetSpellInfo(SpellID)))
+ br.lastSpellCast = SpellID
+ br.lastSpellTarget = br._G.UnitGUID(Unit)
+ --end
+ return true
+ elseif details and details.inconclusive then
+ -- Unable to observe cast-start signals reliably; avoid false failspam.
+ return true
+ else
+ -- Cast failed - reset timer to allow retry
+ br.timersTable[SpellID] = nil
+ -- Optional debug logging
+ -- if shouldPrintCastDebug() then
+ -- printCastFailedDetails("[CAST FAIL][CAST_FAILED] ", SpellID, br.api.wow.GetSpellInfo(SpellID), snapshot, details)
+ -- end
+ return false
+ end
end
end
end
- elseif (FacingCheck == true or br.getFacing("player", Unit) == true) and (br.GetUnitIsUnit("player", Unit) or br.units[Unit] ~= nil or br.getLineOfSight("player", Unit) == true) then
+ elseif (FacingCheck == true or br.functions.unit:getFacing("player", Unit) == true) and (br.functions.unit:GetUnitIsUnit("player", Unit) or br.engines.enemiesEngine.units[Unit] ~= nil or br.functions.misc:getLineOfSight("player", Unit) == true) then
if noCast then
return true
else
+ local snapshot = takeCastStartSnapshot(SpellID)
+
-- currentTarget = UnitGUID(Unit) -- Not Used
br.botCast = true
br.botSpell = SpellID
- br._G.CastSpellByName(br._G.GetSpellInfo(SpellID), Unit)
+ br.botUnit = Unit
+ br._G.CastSpellByName(br.api.wow.GetSpellInfo(SpellID), Unit)
if br._G.IsAoEPending() then
local X, Y, Z = br._G.ObjectPosition(Unit)
br._G.ClickPosition(X, Y, Z)
end
- --if br.getOptionCheck("Start/Stop BadRotations") then
- br.mainButton:SetNormalTexture(select(3, br._G.GetSpellInfo(SpellID)))
- br.lastSpellCast = SpellID
- br.lastSpellTarget = br._G.UnitGUID(Unit)
- --end
- return true
+ local castSucceeded, details = verifyCastStarted(SpellID, snapshot)
+
+ if castSucceeded then
+ --if br.functions.misc:getOptionCheck("Start/Stop BadRotations") then
+ br.ui.toggles.mainButton:SetNormalTexture(select(3, br.api.wow.GetSpellInfo(SpellID)))
+ br.lastSpellCast = SpellID
+ br.lastSpellTarget = br._G.UnitGUID(Unit)
+ --end
+ return true
+ elseif details and details.inconclusive then
+ return true
+ else
+ -- Cast failed - don't set lastSpellCast
+ -- if shouldPrintCastDebug() then
+ -- printCastFailedDetails("[CAST FAIL][CAST_FAILED] ", SpellID, br.api.wow.GetSpellInfo(SpellID), snapshot, details)
+ -- end
+ return false
+ end
end
end
end
@@ -366,10 +591,10 @@ Tenth noCast True to return True/False instead of casting spell.
]]
-- castSpell("target",12345,true)
-- ( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 )
-function br.castSpellMacro(Unit, SpellID, FacingCheck, MovementCheck, SpamAllowed, KnownSkip, DeadCheck, DistanceSkip,
+function cast:castSpellMacro(Unit, SpellID, FacingCheck, MovementCheck, SpamAllowed, KnownSkip, DeadCheck, DistanceSkip,
usableSkip, noCast)
- if br.GetObjectExists(Unit) and br.betterStopCasting(SpellID) ~= true
- and (not br.GetUnitIsDeadOrGhost(Unit) or DeadCheck) then
+ if br.functions.unit:GetObjectExists(Unit) and br.engines.interrupts:betterStopCasting(SpellID) ~= true
+ and (not br.functions.unit:GetUnitIsDeadOrGhost(Unit) or DeadCheck) then
-- we create an usableSkip for some specific spells like hammer of wrath aoe mode
if usableSkip == nil then usableSkip = false end
-- stop if not enough power for that spell
@@ -379,61 +604,90 @@ function br.castSpellMacro(Unit, SpellID, FacingCheck, MovementCheck, SpamAllowe
-- default noCast to false
if noCast == nil then noCast = false end
-- make sure it is a known spell
- if not (KnownSkip == true or br.isKnown(SpellID)) then return false end
+ if not (KnownSkip == true or br.functions.spell:isKnown(SpellID)) then return false end
-- gather our spell range information
- local spellRange = select(6, br._G.GetSpellInfo(SpellID))
+ local spellRange = select(6, br.api.wow.GetSpellInfo(SpellID))
if DistanceSkip == nil then DistanceSkip = false end
if spellRange == nil or (spellRange < 4 and DistanceSkip == false) then spellRange = 4 end
if DistanceSkip == true then spellRange = 40 end
-- Check unit,if it's player then we can skip facing
- if (Unit == nil or br.GetUnitIsUnit("player", Unit)) or -- Player
- (Unit ~= nil and br.GetUnitIsFriend("player", Unit)) then -- Ally
+ if (Unit == nil or br.functions.unit:GetUnitIsUnit("player", Unit)) or -- Player
+ (Unit ~= nil and br.functions.unit:GetUnitIsFriend("player", Unit)) then -- Ally
FacingCheck = true
- elseif br.isSafeToAttack(Unit) ~= true then -- enemy
+ elseif br.engines.enemiesEngineFunctions:isSafeToAttack(Unit) ~= true then -- enemy
return false
end
-- if MovementCheck is nil or false then we dont check it
- if MovementCheck == false or br.isMoving("player") ~= true
+ if MovementCheck == false or br.functions.misc:isMoving("player") ~= true
-- skip movement check during spiritwalkers grace and aspect of the fox
- or br.UnitBuffID("player", 79206) ~= nil
+ or br.functions.aura:UnitBuffID("player", 79206) ~= nil
then
-- if ability is ready and in range
- -- if br.getSpellCD(SpellID) < select(4,GetNetStats()) / 1000
- if (br.getSpellCD(SpellID) < select(4, br._G.GetNetStats()) / 1000) and (br.getOptionCheck("Skip Distance Check") or br.getDistance("player", Unit) <= spellRange or DistanceSkip == true or br.inRange(SpellID, Unit)) then
+ -- if br.functions.spell:getSpellCD(SpellID) < select(4,GetNetStats()) / 1000
+ if (br.functions.spell:getSpellCD(SpellID) < select(4, br._G.GetNetStats()) / 1000) and (br.functions.misc:getOptionCheck("Skip Distance Check") or br.functions.range:getDistance("player", Unit) <= spellRange or DistanceSkip == true or br.functions.range:inRange(SpellID, Unit)) then
-- if spam is not allowed
if SpamAllowed == false then
-- get our last/current cast
if br.timersTable == nil or (br.timersTable ~= nil and (br.timersTable[SpellID] == nil or br.timersTable[SpellID] <= br._G.GetTime() - 0.6)) then
- if (FacingCheck == true or br.getFacing("player", Unit) == true) and (br.GetUnitIsUnit("player", Unit) or br.getLineOfSight("player", Unit) == true) then
+ if (FacingCheck == true or br.functions.unit:getFacing("player", Unit) == true) and (br.functions.unit:GetUnitIsUnit("player", Unit) or br.functions.misc:getLineOfSight("player", Unit) == true) then
if noCast then
return true
else
+ local snapshot = takeCastStartSnapshot(SpellID)
+
br.timersTable[SpellID] = br._G.GetTime()
br.currentTarget = br._G.UnitGUID(Unit)
- br._G.RunMacroText("/cast [@" .. Unit .. "] " .. br._G.GetSpellInfo(SpellID))
- --lastSpellCast = SpellID
- -- change main button icon
- --if br.getOptionCheck("Start/Stop BadRotations") then
- br.mainButton:SetNormalTexture(select(3, br._G.GetSpellInfo(SpellID)))
- br.lastSpellCast = SpellID
- br.lastSpellTarget = br._G.UnitGUID(Unit)
- --end
- return true
+ br.botCast = true
+ br.botSpell = SpellID
+ br.botUnit = Unit
+ br._G.RunMacroText("/cast [@" .. Unit .. "] " .. br.api.wow.GetSpellInfo(SpellID))
+ local castSucceeded, details = verifyCastStarted(SpellID, snapshot)
+
+ if castSucceeded then
+ --lastSpellCast = SpellID
+ -- change main button icon
+ --if br.functions.misc:getOptionCheck("Start/Stop BadRotations") then
+ br.ui.toggles.mainButton:SetNormalTexture(select(3, br.api.wow.GetSpellInfo(SpellID)))
+ br.lastSpellCast = SpellID
+ br.lastSpellTarget = br._G.UnitGUID(Unit)
+ --end
+ return true
+ elseif details and details.inconclusive then
+ return true
+ else
+ -- Cast failed - reset timer to allow retry
+ br.timersTable[SpellID] = nil
+ return false
+ end
end
end
end
- elseif (FacingCheck == true or br.getFacing("player", Unit) == true) and (br.GetUnitIsUnit("player", Unit) or br.getLineOfSight("player", Unit) == true) then
+ elseif (FacingCheck == true or br.functions.unit:getFacing("player", Unit) == true) and (br.functions.unit:GetUnitIsUnit("player", Unit) or br.functions.misc:getLineOfSight("player", Unit) == true) then
if noCast then
return true
else
+ local snapshot = takeCastStartSnapshot(SpellID)
br.currentTarget = br._G.UnitGUID(Unit)
- br._G.RunMacroText("/cast [@" .. Unit .. "] " .. br._G.GetSpellInfo(SpellID))
- --if br.getOptionCheck("Start/Stop BadRotations") then
- br.mainButton:SetNormalTexture(select(3, br._G.GetSpellInfo(SpellID)))
- br.lastSpellCast = SpellID
- br.lastSpellTarget = br._G.UnitGUID(Unit)
- --end
- return true
+ br.botCast = true
+ br.botSpell = SpellID
+ br.botUnit = Unit
+ br._G.RunMacroText("/cast [@" .. Unit .. "] " .. br.api.wow.GetSpellInfo(SpellID))
+ local castSucceeded, details = verifyCastStarted(SpellID, snapshot)
+
+ if castSucceeded then
+ br.ui.toggles.mainButton:SetNormalTexture(select(3, br.api.wow.GetSpellInfo(SpellID)))
+ br.lastSpellCast = SpellID
+ br.lastSpellTarget = br._G.UnitGUID(Unit)
+ return true
+ end
+ if details and details.inconclusive then
+ return true
+ end
+
+ -- if shouldPrintCastDebug() then
+ -- printCastFailedDetails("[CAST FAIL][CAST_FAILED] ", SpellID, br.api.wow.GetSpellInfo(SpellID), snapshot, details)
+ -- end
+ return false
end
end -- End Spam Check
end -- End CD/Distance Check
@@ -443,18 +697,18 @@ function br.castSpellMacro(Unit, SpellID, FacingCheck, MovementCheck, SpamAllowe
end
-- Used in openers
-function br.castOpener(spellIndex, flag, index, checkdistance)
+function cast:castOpener(spellIndex, flag, index, checkdistance)
local spellCast = br.player.spells[spellIndex]
local castable = br.player.cast.able[spellIndex]
local castSpell = br.player.cast[spellIndex]
- local spellName = select(1, br._G.GetSpellInfo(spellCast))
- local maxRange = select(6, br._G.GetSpellInfo(spellCast))
+ local spellName = select(1, br.api.wow.GetSpellInfo(spellCast))
+ local maxRange = select(6, br.api.wow.GetSpellInfo(spellCast))
local cooldown = br.player.cd[spellIndex].remain()
if not maxRange or maxRange == 0 then maxRange = 5 end
if checkdistance == nil then checkdistance = true end
- if not checkdistance or br.getDistance("target") < maxRange then
+ if not checkdistance or br.functions.range:getDistance("target") < maxRange then
if (not castable() and (cooldown == 0 or cooldown > br.player.gcdMax)) then
- br.castOpenerFail(spellName, flag, index)
+ br.functions.cast:castOpenerFail(spellName, flag, index)
-- Print(index..": "..spellName.." (Uncastable)");
-- br._G[flag] = true;
-- return true
@@ -473,18 +727,15 @@ function br.castOpener(spellIndex, flag, index, checkdistance)
end
end
-function br.castOpenerFail(spellName, flag, index)
+function cast:castOpenerFail(spellName, flag, index)
if br.player.opener[flag] == nil then
br._G.print(index .. ": " .. spellName .. " (Uncastable)")
br.player.opener[flag] = true
- elseif br.player.opener[flag] ~= true then
- br._G.print(index .. ": " .. spellName .. " (Uncastable)")
- br.player.opener[flag] = true
end
return true
end
-function br.castMouseoverHealing(Class)
+function cast:castMouseoverHealing(Class)
if br._G.UnitAffectingCombat("player") then
local spellTable = {
["Druid"] = { heal = 8936, dispel = 88423 }
@@ -500,7 +751,7 @@ function br.castMouseoverHealing(Class)
-- local dispelid = spellTable[Class].dispel
for i = 1, #SpecialTargets do
local target = SpecialTargets[i]
- if br.GetUnitExists(target) and not br._G.UnitIsPlayer(target) then
+ if br.functions.unit:GetUnitExists(target) and not br._G.UnitIsPlayer(target) then
local npcID = tonumber(string.match(br._G.UnitGUID(target), "-(%d+)-%x+$"))
for j = 1, #npcTable do
if npcID == npcTable[j] then
@@ -509,7 +760,7 @@ function br.castMouseoverHealing(Class)
local buff, _, _, bufftype = br._G.UnitDebuff(target, n)
if buff then
if bufftype == "Magic" or bufftype == "Curse" or bufftype == "Poison" then
- if br.castSpell(target, 88423, true, false) then
+ if br.functions.cast:castSpell(target, 88423, true, false) then
return
end
end
@@ -518,9 +769,9 @@ function br.castMouseoverHealing(Class)
end
end
-- Heal
- local npcHP = br.getHP(target)
+ local npcHP = br.functions.unit:getHP(target)
if npcHP < 100 then
- if br.castSpell(target, spellTable[Class].heal, true) then
+ if br.functions.cast:castSpell(target, spellTable[Class].heal, true) then
return
end
end
@@ -531,7 +782,7 @@ function br.castMouseoverHealing(Class)
end
end
-function br.isCastingTime(lagTolerance)
+function cast:isCastingTime(lagTolerance)
lagTolerance = lagTolerance or 0
if br._G.UnitCastingInfo("player") ~= nil then
if select(5, br._G.UnitCastingInfo("player")) - br._G.GetTime() <= lagTolerance then
@@ -541,7 +792,7 @@ function br.isCastingTime(lagTolerance)
if select(5, br._G.UnitChannelInfo("player")) - br._G.GetTime() <= lagTolerance then
return true
end
- elseif (br._G.C_Spell.GetSpellCooldown(br._G.GetSpellInfo(61304)) ~= nil and br._G.C_Spell.GetSpellCooldown(br._G.GetSpellInfo(61304)).duration <= lagTolerance) then
+ elseif br.functions.spell:getGlobalCD() <= lagTolerance then
return true
else
return false
@@ -549,21 +800,21 @@ function br.isCastingTime(lagTolerance)
end
-- if getCastTime("Healing Touch")<3 then
-function br.getCastTime(spellID)
+function cast:getCastTime(spellID)
if spellID == 202767 then
- if select(3, br._G.GetSpellInfo(202767)) == 1392545 then
+ if select(3, br.api.wow.GetSpellInfo(202767)) == 1392545 then
spellID = 202767
- elseif select(3, br._G.GetSpellInfo(202767)) == 1392543 then
+ elseif select(3, br.api.wow.GetSpellInfo(202767)) == 1392543 then
spellID = 202768
- elseif select(3, br._G.GetSpellInfo(202767)) == 1392542 then
+ elseif select(3, br.api.wow.GetSpellInfo(202767)) == 1392542 then
spellID = 202771
end
end
- local castTime = select(4, br._G.GetSpellInfo(spellID)) / 1000
+ local castTime = select(4, br.api.wow.GetSpellInfo(spellID)) / 1000
return castTime
end
-function br.getCastTimeRemain(unit)
+function cast:getCastTimeRemain(unit)
if br._G.UnitCastingInfo(unit) ~= nil then
return select(5, br._G.UnitCastingInfo(unit)) / 1000 - br._G.GetTime()
elseif br._G.UnitChannelInfo(unit) ~= nil then
@@ -574,36 +825,37 @@ function br.getCastTimeRemain(unit)
end
-- if isCasting() == true then
-function br.castingUnit(Unit)
+function cast:castingUnit(Unit)
if Unit == nil then Unit = "player" end
if br._G.UnitCastingInfo(Unit) ~= nil
or br._G.UnitChannelInfo(Unit) ~= nil
- or (br._G.C_Spell.GetSpellCooldown(61304) ~= nil and br._G.C_Spell.GetSpellCooldown(61304).duration > 0.001) then
+ or br.functions.spell:getGlobalCD() > 0.001 then
return true
else
return false
end
end
--- if br.isCastingSpell(12345) == true then
-function br.isCastingSpell(spellID, unit)
+-- if br.functions.cast:isCastingSpell(12345) == true then
+function cast:isCastingSpell(spellID, unit)
if unit == nil then unit = "player" end
- local spellName = br._G.GetSpellInfo(spellID)
- local spellCasting = br._G.UnitCastingInfo(unit)
- if spellCasting == nil then
- spellCasting = br._G.UnitChannelInfo(unit)
+ -- Check regular cast (UnitCastingInfo returns spellID as 9th value)
+ local _, _, _, _, _, _, _, _, castingSpellID = br._G.UnitCastingInfo(unit)
+ if castingSpellID == spellID then
+ return true
end
- if tostring(spellCasting) == tostring(spellName) then
+ -- Check channel (UnitChannelInfo returns spellID as 8th value)
+ local _, _, _, _, _, _, _, channelingSpellID = br._G.UnitChannelInfo(unit)
+ if channelingSpellID == spellID then
return true
- else
- return false
end
+ return false
end
-- if isCasting(12345,"target") then
-function br.isCasting(SpellID, Unit)
- if br.GetUnitIsVisible(Unit) and br._G.UnitCastingInfo(Unit) then
- if br._G.UnitCastingInfo(Unit) == br._G.GetSpellInfo(SpellID) then
+function cast:isCasting(SpellID, Unit)
+ if br.functions.unit:GetUnitIsVisible(Unit) and br._G.UnitCastingInfo(Unit) then
+ if br._G.UnitCastingInfo(Unit) == br.api.wow.GetSpellInfo(SpellID) then
return true
end
else
@@ -611,8 +863,8 @@ function br.isCasting(SpellID, Unit)
end
end
--- if br.isCastingSpell(12345) == true then
-function br.isUnitCasting(unit)
+-- if br.functions.cast:isCastingSpell(12345) == true then
+function cast:isUnitCasting(unit)
if unit == nil then unit = "player" end
local spellCasting = br._G.UnitCastingInfo(unit)
if spellCasting == nil then
@@ -626,31 +878,42 @@ function br.isUnitCasting(unit)
end
local castTimers
-function br.createCastFunction(thisUnit, castType, minUnits, effectRng, spellID, index, predict, predictPad, enemies,
+local rangeDelayTimers -- Track when spells first become in range
+function cast:createCastFunction(thisUnit, castType, minUnits, effectRng, spellID, index, predict, predictPad, enemies,
debug)
+ if type(spellID) == "table" then
+ -- return highest rank spell that is known
+ for i = #spellID, 1, -1 do
+ if br.functions.spell:isKnown(spellID[i]) then
+ spellID = spellID[i]
+ break
+ end
+ end
+ end
-- Invalid Spell ID Check
- if br._G.GetSpellInfo(spellID) == nil then br._G.print("Invalid Spell ID: " .. spellID .. " for key: " .. index) end
+ if spellID == nil or br.api.wow.GetSpellInfo(spellID) == nil then br._G.print("Invalid Spell ID: " .. tostring(spellID) .. " for key: " .. index) end
local spellCast = spellID
- local baseSpellID = br._G.FindBaseSpellByID(spellID)
+ local baseSpellID = br.api.wow.FindBaseSpellByID(spellID)
local overrideSpellID = br._G.FindSpellOverrideByID(spellID)
- local baseSpellName = br._G.GetSpellInfo(baseSpellID)
- local spellName, _, icon, castTime, minRange, maxRange = br._G.GetSpellInfo(spellID)
- local spellType = br.getSpellType(baseSpellName)
+ local baseSpellName = br.api.wow.GetSpellInfo(baseSpellID)
+ local spellName, _, icon, castTime, minRange, maxRange = br.api.wow.GetSpellInfo(spellID)
+ local spellType = br.functions.spell:getSpellType(baseSpellName)
if castTimers == nil then castTimers = {} end
if castTimers[spellID] == nil then castTimers[spellID] = br._G.GetTime() end
- if maxRange == 0 then _, _, _, _, minRange, maxRange = br._G.GetSpellInfo(baseSpellID) end
+ if rangeDelayTimers == nil then rangeDelayTimers = {} end
+ if maxRange == 0 then _, _, _, _, minRange, maxRange = br.api.wow.GetSpellInfo(baseSpellID) end
-- Quaking helper - M+ Affix
- if br.getOptionCheck("Quaking Helper") then
+ if br.functions.misc:getOptionCheck("Quaking Helper") then
--Detect channels
local channeledSpell = false
- local costTable = br._G.C_Spell.GetSpellPowerCost(spellID)
+ local costTable = br._G.C_Spell.GetSpellPowerCost(spellID) or {}
for _, costInfo in pairs(costTable) do
if costInfo.costPerSec > 0 then
channeledSpell = true
end
end
-- Quake check
- local quakeRemain = br.getDebuffRemain("player", 240448)
+ local quakeRemain = br.functions.aura:getDebuffRemain("player", 240448)
if quakeRemain > 0 then
if (castTime > 0 and quakeRemain <= ((castTime + 300) / 1000)) or (castTime == 0 and channeledSpell and quakeRemain < 1.5) then
return false
@@ -658,7 +921,7 @@ function br.createCastFunction(thisUnit, castType, minUnits, effectRng, spellID,
end
end
--If we want to predict movement, include casttime, else 0 it
- if predict ~= nil then castTime = castTime / 1000 else castTime = 0 end
+ if predict then castTime = castTime / 1000 else castTime = 0 end
if predictPad then
castTime = castTime + predictPad
end
@@ -690,132 +953,287 @@ function br.createCastFunction(thisUnit, castType, minUnits, effectRng, spellID,
enemies = #br.player.enemies["yards" .. effectRng .. "t"]
end
end
+ if castType == "targetAOE" then
+ if br.player.enemies["yards" .. effectRng .. "t"] ~= nil then
+ enemies = #br.player.enemies["yards" .. effectRng .. "t"]
+ end
+ end
end
-- Lighter Cast Spell
+ -- local function castingSpell(thisUnit, spellID, spellName, icon, castType, printReport, debug)
+ -- if br._G.UnitHealth(thisUnit) > 0 or castType == "dead" then
+ -- -- Debug Only
+ -- if debug then return true end
+
+ -- -- Store pre-cast state to verify cast success
+ -- local gcdBefore = br.functions.spell:getSpellCD(61304) -- GCD spell
+ -- local spellCDBefore = br.functions.spell:getSpellCD(spellID) -- This spell's CD
+ -- local castingBefore = br._G.UnitCastingInfo("player") ~= nil
+ -- local channelingBefore = br._G.UnitChannelInfo("player") ~= nil
+ -- local prevLastCastTime = br.functions.lastCast.lastCastTable.castTime[spellID] or 0
+
+ -- -- Cast Spell
+ -- br.botCast = true -- Used by old Queue Cast
+ -- br.botSpell = spellID -- Used by old Queue Cast
+ -- -- Condemn Patch (Blizz is an small indie developer!)
+ -- if spellID == br.player.spells.condemn or spellID == br.player.spells.condemnMassacre then
+ -- spellName = br.api.wow.GetSpellInfo(br.player.spells.execute)
+ -- end
+ -- -- br._G.print("Spell: "..tostring(spellName).." - UnitName: "..tostring(br._G.UnitName(thisUnit)).." - Unit: "..tostring(thisUnit))
+ -- br._G.CastSpellByName(spellName, thisUnit)
+ -- if br._G.IsAoEPending() then
+ -- local X, Y, Z = br._G.ObjectPosition(thisUnit)
+ -- br._G.ClickPosition(X, Y, Z)
+ -- end
+
+ -- -- Verify cast actually started
+ -- local gcdAfter = br.functions.spell:getSpellCD(61304)
+ -- local spellCDAfter = br.functions.spell:getSpellCD(spellID)
+ -- local castingAfter = br._G.UnitCastingInfo("player") ~= nil
+ -- local channelingAfter = br._G.UnitChannelInfo("player") ~= nil
+ -- -- Cast succeeded if: GCD triggered OR spell CD triggered OR started casting OR started channeling
+ -- local castSucceeded = (gcdAfter > gcdBefore) or
+ -- (spellCDAfter > spellCDBefore) or
+ -- (castingAfter and not castingBefore) or
+ -- (channelingAfter and not channelingBefore)
+
+ -- -- Fallback: check lastCastTable timestamp updates for instant/toggle spells
+ -- local newLastCastTime = br.functions.lastCast.lastCastTable.castTime[spellID]
+ -- if not castSucceeded and newLastCastTime and newLastCastTime ~= 0 and newLastCastTime ~= prevLastCastTime and (br._G.GetTime() - newLastCastTime) < 1 then
+ -- castSucceeded = true
+ -- end
+
+ -- if castSucceeded then
+ -- -- add to cast timer
+ -- castTimers[spellID] = br._G.GetTime() + 1
+ -- -- change main button icon
+ -- br.ui.toggles.mainButton:SetNormalTexture(icon)
+ -- -- Update Last Cast
+ -- br.lastSpellCast = spellID
+ -- br.lastSpellTarget = br._G.UnitGUID(thisUnit)
+ -- return true
+ -- else
+ -- -- Cast failed - don't update timers or last cast
+ -- -- Optional debug logging
+ -- if br.functions.misc and (br.functions.misc:isChecked("Cast Debug") or br.functions.misc:isChecked("Display Failcasts")) then
+ -- br._G.print("[CAST DEBUG] Failed createCastFunction attempt: SpellID=" .. tostring(spellID) .. " Name=" .. tostring(spellName))
+ -- br._G.print(" GCD before/after: " .. tostring(gcdBefore) .. " -> " .. tostring(br.functions.spell:getSpellCD(61304)))
+ -- br._G.print(" SpellCD before/after: " .. tostring(spellCDBefore) .. " -> " .. tostring(br.functions.spell:getSpellCD(spellID)))
+ -- br._G.print(" Casting before/after: " .. tostring(castingBefore) .. " -> " .. tostring(br._G.UnitCastingInfo("player") ~= nil))
+ -- br._G.print(" Channeling before/after: " .. tostring(channelingBefore) .. " -> " .. tostring(br._G.UnitChannelInfo("player") ~= nil))
+ -- br._G.print(" PrevLastCastTime/newLastCastTime: " .. tostring(prevLastCastTime) .. " -> " .. tostring(br.functions.lastCast.lastCastTable.castTime[spellID]))
+ -- end
+ -- return false
+ -- end
+ -- end
+ -- return false
+ -- end
local function castingSpell(thisUnit, spellID, spellName, icon, castType, printReport, debug)
if br._G.UnitHealth(thisUnit) > 0 or castType == "dead" then
- -- Debug Only
- if debug then return true end
+ -- Debug Only (cast.able check mode)
+ if debug then
+ return true
+ end
+
+ -- Hard CC (stun/fear/silence/etc): don't spam cast attempts for spells that can't be used.
+ if br.functions.combat and br.functions.combat.cannotCast and br.functions.combat:cannotCast(spellID) then
+ castTimers[spellID] = br._G.GetTime() + 0.50
+ setCastIntentLock(spellID, 0.50) -- Lock intent during hard CC
+ return printReport(false, "No Control")
+ end
+
+ -- Set cast intent lock to prevent rapid re-casting attempts
+ setCastIntentLock(spellID, 0.20)
+
+ local snapshot = takeCastStartSnapshot(spellID)
+
-- Cast Spell
br.botCast = true -- Used by old Queue Cast
br.botSpell = spellID -- Used by old Queue Cast
+ br.botUnit = thisUnit
-- Condemn Patch (Blizz is an small indie developer!)
- if spellID == br.player.spells.condemn or spellID == br.player.spells.condemnMassacre then
- spellName = br._G
- .GetSpellInfo(br.player.spells.execute)
+ if br.player and br.player.spells and (spellID == br.player.spells.condemn or spellID == br.player.spells.condemnMassacre) then
+ spellName = br.api.wow.GetSpellInfo(br.player.spells.execute)
end
- -- br._G.print("Spell: "..tostring(spellName).." - UnitName: "..tostring(br._G.UnitName(thisUnit)).." - Unit: "..tostring(thisUnit))
br._G.CastSpellByName(spellName, thisUnit)
if br._G.IsAoEPending() then
local X, Y, Z = br._G.ObjectPosition(thisUnit)
br._G.ClickPosition(X, Y, Z)
end
- -- add to cast timer
- castTimers[spellID] = br._G.GetTime() + 1
- -- change main button icon
- br.mainButton:SetNormalTexture(icon)
- -- Update Last Cast
- br.lastSpellCast = spellID
- br.lastSpellTarget = br._G.UnitGUID(thisUnit)
- return true
+
+ local castSucceeded, details = verifyCastStarted(spellID, snapshot, spellName)
+
+ if castSucceeded then
+ -- add to cast timer
+ castTimers[spellID] = br._G.GetTime() + 1
+ local sameSpellCastRemain = getSameSpellCastRemain(spellID)
+ if sameSpellCastRemain > 0 then
+ setCastIntentLock(spellID, sameSpellCastRemain + 0.12)
+ else
+ local settleLock = 0.12
+ if spellType == "Helpful" then
+ settleLock = 0.30
+ elseif spellType == "Harmful" and not br._G.UnitAffectingCombat("player") then
+ settleLock = 0.30
+ end
+ setCastIntentLock(spellID, settleLock)
+ end
+ -- change main button icon
+ br.ui.toggles.mainButton:SetNormalTexture(icon)
+ -- Update Last Cast
+ br.lastSpellCast = spellID
+ br.lastSpellTarget = br._G.UnitGUID(thisUnit)
+ return true
+ end
+
+ if details and details.inconclusive then
+ -- Cast may have been queued/started but signals didn't update yet.
+ -- Throttle quick retries, but don't emit CAST_FAILED spam.
+ castTimers[spellID] = br._G.GetTime() + 0.10
+ setCastIntentLock(spellID, 0.20) -- Lock intent on inconclusive result
+ return true
+ end
+
+ -- Failed to start casting: allow a quick retry, but don't lock out for 1s.
+ castTimers[spellID] = br._G.GetTime() + 0.10
+ setCastIntentLock(spellID, 0.20) -- Lock intent on failed cast
+ -- if shouldPrintCastDebug() and not debug then
+ -- printCastFailedDetails("[CAST FAIL][CAST_FAILED] ", spellID, spellName, snapshot, details)
+ -- end
+ return printReport(false, "Cast Failed")
end
return false
end
-- Talent Check
local function hasTalent(spellID)
for k, v in pairs(br.player.spells.talents) do
- if spellID == v then return br.player.talent[k] or br.isKnown(spellID) end
+ if spellID == v then return br.player.talent[k] or br.functions.spell:isKnown(spellID) end
end
return true
end
- -- Essence Check - BfA
- local function hasEssence()
- local essence = br.player.essence
- if essence[index] == nil then return true end
- if essence[index].id == nil then return true end
- return essence[index].active
+ -- -- Essence Check - BfA
+ -- local function hasEssence()
+ -- local essence = br.player.essence
+ -- if essence[index] == nil then return true end
+ -- if essence[index].id == nil then return true end
+ -- return essence[index].active
+ -- end
+ -- -- Queen's Court - BfA
+ -- local function queensCourtCastCheck(spellID)
+ -- local queensCourtEncounter = br.functions.aura:UnitDebuffID("player", 304409) -- EJ_GetEncounterInfo(2311)
+ -- return queensCourtEncounter == nil or (queensCourtEncounter ~= nil and br.functions.lastCast.lastCastTable.tracker[1] ~= spellID)
+ -- end
+ -- Base Spell Availablility Check
+ if br.functions.lastCast.lastCastTable.castTime[spellID] == nil then
+ br.functions.lastCast.lastCastTable.castTime[spellID] = br._G.GetTime() -
+ (br.functions.spell:getGlobalCD(true) + (select(3, br._G.GetNetStats()) / 100))
end
- -- Queen's Court - BfA
- local function queensCourtCastCheck(spellID)
- local queensCourtEncounter = br.UnitDebuffID("player", 304409) -- EJ_GetEncounterInfo(2311)
- return queensCourtEncounter == nil or (queensCourtEncounter ~= nil and br.lastCastTable.tracker[1] ~= spellID)
+ -- Never allow same-spell recast while it's currently being cast/channelled.
+ if cast:isCastingSpell(spellID, "player") then
+ return false
end
- -- Base Spell Availablility Check
- if br.lastCastTable.castTime[spellID] == nil then
- br.lastCastTable.castTime[spellID] = br._G.GetTime() -
- (br.getGlobalCD(true) + (select(3, br._G.GetNetStats()) / 100))
+ -- Cast Intent Lock: Prevent multiple cast attempts in rapid succession
+ -- Only check when NOT in debug mode (actual casting, not ability checking)
+ if not debug and isCastIntentLocked(spellID) then
+ return false
end
if (baseSpellID == spellID or overrideSpellID == spellID) and (br.empowerID == nil or br.empowerID == 0)
- and (br._G.GetTime() - br.lastCastTable.castTime[spellID] > br.getGlobalCD(true) + (select(3, br._G.GetNetStats()) / 100)) --br.getGlobalCD(true)) -- Double Casting Check
+ and (br._G.GetTime() - br.functions.lastCast.lastCastTable.castTime[spellID] > br.functions.spell:getGlobalCD(true) + (select(3, br._G.GetNetStats()) / 100)) --br.functions.spell:getGlobalCD(true)) -- Double Casting Check
and ((thisUnit ~= nil and not br._G.UnitIsUnit(thisUnit, "player") and not br._G.UnitIsFriend(thisUnit, "player")
- and (br.getCastTime(spellID) <= br.getGlobalCD(true) or br.getCastTime(spellID) < br.getTTD(thisUnit)))
+ and (cast:getCastTime(spellID) <= br.functions.spell:getGlobalCD(true) or cast:getCastTime(spellID) < br.engines.ttdTable:getTTD(thisUnit)))
or thisUnit == nil or br._G.UnitIsUnit(thisUnit, "player") or br._G.UnitIsFriend(thisUnit, "player")) -- Dies Soon Check
and ((thisUnit ~= nil and br._G.UnitIsUnit(thisUnit, "target") and br._G.C_Spell.IsSpellUsable(spellID)) or true) and not select(2, br._G.C_Spell.IsSpellUsable(spellID)) -- Usability Checks
- and (castTimers[spellID] < br._G.GetTime()) and br.getSpellCD(spellID) <= br:getUpdateRate()
- and (br.getSpellCD(61304) <= 0 or select(2, br._G.GetSpellBaseCooldown(spellID)) <= 0
- or (br.getCastTime(spellID) > 0 and br.getCastTimeRemain("player") <= br:getUpdateRate())) -- Cooldown Checks
- and (br.isKnown(spellID) or castType == "known" or spellID == br.player.spells.condemn or spellID == br.player.spells.condemnMassacre) -- Known/Current Checks
- and hasTalent(spellID) and hasEssence() and not br.isIncapacitated(spellID) and queensCourtCastCheck(spellID)
- and (not (br._G.C_Spell.IsAutoRepeatSpell(br._G.GetSpellInfo(spellID)) or (spellID == 6603 and br._G.C_Spell.IsCurrentSpell(spellID)))) --[[and not br.hasNoControl(spellID)]] -- Talent/Essence/Incapacitated/Special Checks
+ and (castTimers[spellID] < br._G.GetTime()) and br.functions.spell:getSpellCD(spellID) <= br.engines:getUpdateRate()
+ and (br.functions.spell:getGlobalCD() <= 0
+ or br.functions.spell:getSpellCD(spellID) <= 0
+ or (select(2, br._G.GetSpellBaseCooldown(spellID)) or 0) <= 0 -- Spell has no base cooldown (only GCD)
+ or (br.functions.cast:getCastTime(spellID) > 0 and br.functions.cast:getCastTimeRemain("player") <= br.engines:getUpdateRate())) -- Cooldown Checks
+ and (br.functions.spell:isKnown(spellID) or castType == "known" or spellID == br.player.spells.condemn or spellID == br.player.spells.condemnMassacre) -- Known/Current Checks
+ and hasTalent(spellID) --[[and hasEssence()]]
+ and not (br.functions.combat and br.functions.combat.cannotCast and br.functions.combat:cannotCast(spellID))
+ and not br.functions.combat:isIncapacitated(spellID) -- legacy behavior; cannotCast() handles hard CC
+ and (not (br._G.C_Spell.IsAutoRepeatSpell(br.api.wow.GetSpellInfo(spellID)) or (spellID == 6603 and br._G.C_Spell.IsCurrentSpell(spellID)))) --[[and not br.functions.combat:hasNoControl(spellID)]] -- Talent/Essence/Incapacitated/Special Checks
and (thisUnit == nil or castType ~= "dead" or (thisUnit ~= nil and castType == "dead" and br._G.UnitIsDeadOrGhost(thisUnit))) -- Dead Check
or spellID == br.empowerID
then
if castType == "known" then castType = "norm" end
local function printReport(debugOnly, debugReason, thisCount)
if debugReason == nil then debugReason = "" end
- if ((br.isChecked("Display Failcasts") and not debugOnly) or br.isChecked("Cast Debug")) and not debug then
+ local canonicalCodes = {
+ ["No Unit"] = "NO_UNIT",
+ ["Below Min Units"] = "MIN_UNITS",
+ ["Below Min Units Facing"] = "MIN_UNITS_FACING",
+ ["Below Min Units Cone"] = "MIN_UNITS_CONE",
+ ["Below Min Units Rect"] = "MIN_UNITS_RECT",
+ ["Not Dead"] = "NOT_DEAD",
+ ["No Range"] = "NO_RANGE",
+ ["Not Safe"] = "NOT_SAFE",
+ ["Invalid Unit"] = "INVALID_UNIT",
+ ["No Control"] = "NO_CONTROL",
+ ["Cast Failed"] = "CAST_FAILED",
+ }
+ local reasonCode = canonicalCodes[debugReason]
+ if reasonCode == nil then
+ reasonCode = debugReason ~= "" and tostring(debugReason):upper():gsub("[^A-Z0-9]+", "_") or "UNKNOWN"
+ end
+ local prefix = "[CAST FAIL][" .. tostring(reasonCode) .. "] "
+ if ((br.functions.misc:isChecked("Display Failcasts") and not debugOnly) or br.functions.misc:isChecked("Cast Debug")) and not debug then
if debugReason == "No Unit" then
- br.player.ui.debug("Spell: " ..
+ br.player.ui.debug(prefix .. "Spell: " ..
spellName .. " failed to cast because there was not unit found in " .. maxRange .. "yrds.")
elseif debugReason == "Below Min Units" then
- br.player.ui.debug("Spell: " ..
+ br.player.ui.debug(prefix .. "Spell: " ..
spellName ..
" failed to cast because there are " ..
thisCount .. " enemies in " .. maxRange .. "yrds, but " .. minUnits .. " are needed to cast.")
elseif debugReason == "Below Min Units Facing" then
- br.player.ui.debug("Spell: " ..
+ br.player.ui.debug(prefix .. "Spell: " ..
spellName ..
" failed to cast because there are " ..
thisCount .. " enemies in " .. maxRange .. "yrds in front, but " .. minUnits ..
" are needed to cast.")
elseif debugReason == "Below Min Units Cone" then
- br.player.ui.debug("Spell: " ..
+ br.player.ui.debug(prefix .. "Spell: " ..
spellName ..
" failed to cast because there are " ..
thisCount ..
" enemies in " ..
maxRange .. "yrds in an " .. effectRng .. "deg cone, but " .. minUnits .. " are needed to cast.")
elseif debugReason == "Below Min Units Rect" then
- br.player.ui.debug("Spell: " ..
+ br.player.ui.debug(prefix .. "Spell: " ..
spellName ..
" failed to cast because there are " ..
thisCount ..
" enemies in " ..
maxRange .. "yrds by " .. effectRng .. "yrd rectange, but " .. minUnits .. " are needed to cast.")
elseif debugReason == "Not Dead" then
- br.player.ui.debug("Spell: " .. spellName .. " failed to cast because Unit is not dead.")
+ br.player.ui.debug(prefix .. "Spell: " .. spellName .. " failed to cast because Unit is not dead.")
elseif debugReason == "No Range" then
- br.player.ui.debug("Spell: " .. spellName .. " failed to cast because Unit is not in range.")
+ br.player.ui.debug(prefix .. "Spell: " .. spellName .. " failed to cast because Unit is not in range.")
elseif debugReason == "Not Safe" then
- br.player.ui.debug("Spell: " .. spellName .. " failed to cast because it is not safe to aoe.")
+ br.player.ui.debug(prefix .. "Spell: " .. spellName .. " failed to cast because it is not safe to aoe.")
elseif debugReason == "Invalid Unit" then
- if not br._G.UnitIsFriend(thisUnit, "player") and br.units[thisUnit] == nil then
- br.player.ui.debug("Spell: " ..
- spellName .. " failed to cast because Unit is not player, friend, or in br.units.")
- elseif not br.getLineOfSight(thisUnit) then
- br.player.ui.debug("Spell: " .. spellName .. " failed to cast because Unit is out line of sight.")
+ if not br._G.UnitIsFriend(thisUnit, "player") and br.engines.enemiesEngine.units[thisUnit] == nil then
+ br.player.ui.debug(prefix .. "Spell: " ..
+ spellName .. " failed to cast because Unit is not player, friend, or in br.engines.enemiesEngine.units.")
+ elseif not br.functions.misc:getLineOfSight(thisUnit) then
+ br.player.ui.debug(prefix .. "Spell: " .. spellName .. " failed to cast because Unit is out line of sight.")
else
- br.player.ui.debug("Spell: " .. spellName .. " failed to cast for unknown reason.")
+ br.player.ui.debug(prefix .. "Spell: " .. spellName .. " failed to cast for unknown reason.")
end
+ elseif debugReason == "No Control" then
+ br.player.ui.debug(prefix .. "Spell: " .. spellName .. " failed to cast because player is crowd controlled.")
+ elseif debugReason == "Cast Failed" then
+ br.player.ui.debug(prefix .. "Spell: " .. spellName .. " failed to start casting (no GCD/cast/channel detected).")
else
- br._G.print("|cffFF0000Error: |r Failed to cast. - "
+ br._G.print("|cffFF0000Error: |r " .. prefix .. "Failed to cast. - "
.. "Spell: " .. spellName
.. ", ID: " .. spellID
.. ", Type: " .. spellType
.. ", Min Range: " .. minRange
.. ", Max Range: " .. maxRange
- .. ", Distance: " .. br.getDistance(thisUnit)
- .. ", SpellRange: " .. tostring(br._G.C_Spell.IsSpellInRange(spellName, thisUnit) == 1)
+ .. ", Distance: " .. br.functions.range:getDistance(thisUnit)
+ .. ", SpellRange: " .. tostring(br._G.C_Spell.IsSpellInRange(spellName, thisUnit))
.. ", thisUnit: " .. tostring(thisUnit)
)
end
@@ -826,33 +1244,38 @@ function br.createCastFunction(thisUnit, castType, minUnits, effectRng, spellID,
-- local unitAssigned = false
if thisUnit == nil then
if castType == "norm" or castType == "dead" or castType == "rect" or castType == "cone" then
- thisUnit = br.getSpellUnit(baseSpellID, false, minRange, maxRange, spellType)
- elseif castType == "groundCC" or "groundLocation" then
+ thisUnit = br.functions.unit:getSpellUnit(baseSpellID, false, minRange, maxRange, spellType)
+ elseif castType == "groundCC" or castType == "groundLocation" then
return
else
- thisUnit = br.getSpellUnit(baseSpellID, true, minRange, maxRange, spellType)
+ thisUnit = br.functions.unit:getSpellUnit(baseSpellID, true, minRange, maxRange, spellType)
end
-- if thisUnit ~= nil and thisUnit ~= "None" then unitAssigned = true end
end
+ -- CRITICAL: Early exit if we still have nil after attempting to find unit
+ if thisUnit == nil then
+ printReport(true, "No Unit")
+ return false
+ end
-- Cast Ground AOE at "Best" Locaton
if thisUnit == "best" then
if debug then return true end
- return br.castGroundAtBestLocation(spellCast, effectRng, minUnits, maxRange, minRange, castType, castTime)
+ return br.functions.custom:castGroundAtBestLocation(spellCast, effectRng, minUnits, maxRange, minRange, castType, castTime)
end
-- Cast Ground AOE at Player/Target Location
if thisUnit == "playerGround" or thisUnit == "targetGround" or castType == "groundCC" then
local targetUnit
targetUnit = thisUnit == "playerGround" and "player" or "target"
if castType == "groundCC" then targetUnit = thisUnit end
- if (br.getDistance(targetUnit) < maxRange or br._G.C_Spell.IsSpellInRange(spellName, targetUnit) == 1) then
+ if (br.functions.range:getDistance(targetUnit) < maxRange or br._G.C_Spell.IsSpellInRange(spellName, targetUnit) == true) then
if debug then return true end
- return br.castGroundAtUnit(spellCast, effectRng, minUnits, maxRange, minRange, castType, targetUnit)
+ return br.functions.custom:castGroundAtUnit(spellCast, effectRng, minUnits, maxRange, minRange, castType, targetUnit)
end
end
-- Cast Ground AOE at Provide X/Y Location
if thisUnit == "groundLocation" then
if debug then return true end
- return br.castGroundLocation(castType, minUnits, baseSpellID, maxRange, minRange, effectRng)
+ return br.functions.cast:castGroundLocation(castType, minUnits, baseSpellID, maxRange, minRange, effectRng)
end
if thisUnit == "None" then
printReport(true, "No Unit")
@@ -860,18 +1283,57 @@ function br.createCastFunction(thisUnit, castType, minUnits, effectRng, spellID,
end
-- Other Cast Conditions - Require Target
if thisUnit ~= nil and thisUnit ~= "None"
- and (br.GetUnitIsUnit(thisUnit, "player") or br.GetUnitIsFriend(thisUnit, "player") or br.units[thisUnit] ~= nil or br.getLineOfSight(thisUnit))
+ and (br.functions.unit:GetUnitIsUnit(thisUnit, "player")
+ or br.functions.unit:GetUnitIsFriend(thisUnit, "player")
+ or br.engines.enemiesEngine.units[thisUnit] ~= nil
+ or br.functions.misc:getLineOfSight("player", thisUnit))
then
-- Range Check
local inRange = function(minRange, maxRange)
- local distance = castType == "pet" and br.getDistance(thisUnit, "pet") or br.getDistance(thisUnit)
- return br._G.C_Spell.IsSpellInRange(spellName, thisUnit) == 1 or
- (distance >= minRange and distance < maxRange - 1)
+ -- For player target, always consider in range for self-cast spells
+ if thisUnit == "player" then return true end
+
+ -- Check spell range using WoW API first
+ local spellInRange = br._G.C_Spell.IsSpellInRange(spellName, thisUnit)
+
+ -- If definitively out of range, return false immediately
+ if spellInRange == false then
+ -- Reset the range delay timer since we're out of range
+ local timerKey = spellID .. "_" .. (br._G.UnitGUID(thisUnit) or thisUnit)
+ rangeDelayTimers[timerKey] = nil
+ return false
+ end
+
+ if spellInRange == true then
+ -- When moving, require the spell to be in range for a short duration
+ -- to prevent casting at the exact moment it becomes in range
+ if br.functions.misc:isMoving("player") and maxRange > 0 then
+ local currentTime = br._G.GetTime()
+ local timerKey = spellID .. "_" .. (br._G.UnitGUID(thisUnit) or thisUnit)
+
+ if rangeDelayTimers[timerKey] == nil then
+ -- First time seeing this spell in range, start the timer
+ rangeDelayTimers[timerKey] = currentTime
+ return false
+ else
+ -- Check if enough time has passed (150ms delay when moving)
+ local timeInRange = currentTime - rangeDelayTimers[timerKey]
+ if timeInRange < 0.5 then
+ return false -- Not in range long enough yet
+ end
+ end
+ end
+ return true
+ end
+
+ -- If IsSpellInRange returns nil (no range or can't determine), fall back to distance calculation
+ local distance = castType == "pet" and br.functions.range:getDistance(thisUnit, "pet") or br.functions.range:getDistance(thisUnit)
+ return (distance >= minRange and distance < maxRange - 1.5)
end
- if --[[br._G.C_Spell.IsSpellInRange(spellName,thisUnit) == 1 or]] inRange(minRange, maxRange) then
+ if inRange(minRange, maxRange) then
-- Dead Friend
if castType == "dead" then
- if br._G.UnitIsPlayer(thisUnit) and br.GetUnitIsDeadOrGhost(thisUnit) and br.GetUnitIsFriend(thisUnit, "player") then
+ if br._G.UnitIsPlayer(thisUnit) and br.functions.unit:GetUnitIsDeadOrGhost(thisUnit) and br.functions.unit:GetUnitIsFriend(thisUnit, "player") then
return castingSpell(thisUnit, spellID, spellName, icon, castType, printReport, debug)
else
return printReport(false, "Not Dead")
@@ -879,17 +1341,18 @@ function br.createCastFunction(thisUnit, castType, minUnits, effectRng, spellID,
end
-- AOE/ST Casts
-- Cast Ground/Cone/Rectangle/Player AOE
- if (castType == "ground" or castType == "aoe" or castType == "cone" or castType == "rect") then
+ if (castType == "ground" or castType == "aoe" or castType == "cone" or castType == "rect" or castType == "targetAOE") then
-- Failsafe, incase we were unable to retrieve enemies counts
local enemyCount = enemies
- or ((castType == "ground" or castType == "aoe") and #br.getEnemies("player", maxRange))
- or (castType == "cone" and br.getEnemiesInCone(180, effectRng))
- or (castType == "rect" and br.getEnemiesInRect(effectRng, maxRange))
+ or ((castType == "ground" or castType == "aoe") and #br.engines.enemiesEngineFunctions:getEnemies("player", maxRange))
+ or (castType == "cone" and br.engines.enemiesEngineFunctions:getEnemiesInCone(180, effectRng))
+ or (castType == "rect" and br.engines.enemiesEngineFunctions:getEnemiesInRect(effectRng, maxRange))
+ or (castType == "targetAOE" and #br.engines.enemiesEngineFunctions:getEnemies(thisUnit, effectRng))
or 0
- if enemyCount >= minUnits and (br.isSafeToAoE(spellID, thisUnit, effectRng, minUnits, castType, enemyCount) or br.isDummy("target")) then
+ if enemyCount >= minUnits and (br.functions.range:isSafeToAoE(spellID, thisUnit, effectRng, minUnits, castType, enemyCount) or br.functions.unit:isDummy("target")) then
-- if castType == "ground" then
-- if debug then return true end
- -- return br.castGround(thisUnit, spellCast, maxRange, minRange, effectRng, castTime)
+ -- return br.functions.cast:castGround(thisUnit, spellCast, maxRange, minRange, effectRng, castTime)
-- else
return castingSpell(thisUnit, spellID, spellName, icon, castType, printReport, debug)
-- end
@@ -900,12 +1363,12 @@ function br.createCastFunction(thisUnit, castType, minUnits, effectRng, spellID,
end
end
-- Cast Non-AOE
- if (castType == "norm" or castType == "pet") and ((castType == "norm" and br.getFacing("player", thisUnit)) or (castType == "pet" and br.getFacing("pet", thisUnit))
+ if (castType == "norm" or castType == "pet") and ((castType == "norm" and br.functions.unit:getFacing("player", thisUnit)) or (castType == "pet" and br.functions.unit:getFacing("pet", thisUnit))
or spellType == "Helpful" or spellType == "Unknown")
then
-- Failsafe, incase we were unable to retrieve enemies counts
- local enemyFacingCount = enemies or #br.getEnemies("player", maxRange, false, true) or 0
- if (minUnits == 1 --[[and br._G.C_Spell.IsSpellInRange(spellName, thisUnit) == 1]]) or (enemyFacingCount >= minUnits) or spellType == "Helpful" or spellType == "Unknown" then
+ local enemyFacingCount = enemies or #br.engines.enemiesEngineFunctions:getEnemies("player", maxRange, false, true) or 0
+ if (minUnits == 1 --[[and br._G.C_Spell.IsSpellInRange(spellName, thisUnit) == true]]) or (enemyFacingCount >= minUnits) or spellType == "Helpful" or spellType == "Unknown" then
return castingSpell(thisUnit, spellID, spellName, icon, castType, printReport, debug)
else
return printReport(false, "Below Min Units Facing", enemyFacingCount)
@@ -924,7 +1387,7 @@ function br.createCastFunction(thisUnit, castType, minUnits, effectRng, spellID,
end
-- Cast Spell Queue
-function br.castQueue()
+function cast:castQueue()
-- Catch for spells not registering on Combat log
if br.player ~= nil then
if br.player.queue ~= nil and #br.player.queue > 0 and not br._G.IsAoEPending() then
@@ -934,7 +1397,7 @@ function br.castQueue()
local minUnits = br.player.queue[i].minUnits
local effectRng = br.player.queue[i].effectRng
local spellID = br.player.queue[i].id
- if br.createCastFunction(thisUnit, debug, minUnits, effectRng, spellID) then return end
+ if br.functions.cast:createCastFunction(thisUnit, debug, minUnits, effectRng, spellID) then return end
end
end
end
diff --git a/System/Functions/Combat.lua b/System/Functions/Combat.lua
index c5e9e39863..d00c885f40 100644
--- a/System/Functions/Combat.lua
+++ b/System/Functions/Combat.lua
@@ -1,27 +1,25 @@
local _, br = ...
-function br.canAoE(unit, distance)
- local notValid = false
+br.functions.combat = br.functions.combat or {}
+local combat = br.functions.combat
+
+function combat:canAoE(unit, distance)
if unit == nil then
return false
end
if distance == nil then
distance = 8
end
- for i = 1, #br.getEnemies(unit, distance) do
- local thisUnit = br.getEnemies(unit, distance)[i]
- if not br.isValidUnit(thisUnit) then
- notValid = true
- break
+ local enemies = br.engines.enemiesEngineFunctions:getEnemies(unit, distance)
+ for i = 1, #enemies do
+ if not br.functions.misc:isValidUnit(enemies[i]) then
+ return false
end
end
- if notValid then
- return false
- end
return true
end
-- if canAttack("player","target") then
-function br.canAttack(Unit1, Unit2)
+function combat:canAttack(Unit1, Unit2)
if Unit1 == nil then
Unit1 = "player"
end
@@ -31,12 +29,12 @@ function br.canAttack(Unit1, Unit2)
return br._G.UnitCanAttack(Unit1, Unit2)
end
-function br.canDisarm(Unit)
+function combat:canDisarm(Unit)
if br.DisarmedTarget == nil then
br.DisarmedTarget = 0
end
if br.isDisarmed == true then
- if br.GetUnitExists(Unit) and br._G.UnitGUID(Unit) ~= br.DisarmedTarget then
+ if br.functions.unit:GetUnitExists(Unit) and br._G.UnitGUID(Unit) ~= br.DisarmedTarget then
br.DisarmedTarget = br._G.UnitGUID(Unit)
return false
else
@@ -44,18 +42,18 @@ function br.canDisarm(Unit)
return true
end
end
- if not br.isInCombat("player") or br.GetUnitExists(Unit) then
- if not br.isInCombat("player") or br._G.UnitGUID(Unit) ~= br.DisarmedTarget then
+ if not br.functions.misc:isInCombat("player") or br.functions.unit:GetUnitExists(Unit) then
+ if not br.functions.misc:isInCombat("player") or br._G.UnitGUID(Unit) ~= br.DisarmedTarget then
br.isDisarmed = false
return true
end
end
end
--- if br.getCombatTime() <= 5 then
-function br.getCombatTime()
- local combatStarted = br.data.settings[br.selectedSpec]["Combat Started"]
- local combatTime = br.data.settings[br.selectedSpec]["Combat Time"]
+-- if br.functions.combat:getCombatTime() <= 5 then
+function combat:getCombatTime()
+ local combatStarted = br.data.settings[br.loader.selectedSpec]["Combat Started"]
+ local combatTime = br.data.settings[br.loader.selectedSpec]["Combat Time"]
if combatStarted == nil then
return 0
end
@@ -67,12 +65,12 @@ function br.getCombatTime()
else
combatTime = 0
end
- br.data.settings[br.selectedSpec]["Combat Time"] = combatTime
+ br.data.settings[br.loader.selectedSpec]["Combat Time"] = combatTime
return (math.floor(combatTime * 1000) / 1000)
end
-function br.getOoCTime()
- local combatStarted = br.data.settings[br.selectedSpec]["Combat Started"]
+function combat:getOoCTime()
+ local combatStarted = br.data.settings[br.loader.selectedSpec]["Combat Started"]
local combatTime
if combatStarted ~= nil then
return br._G.GetTime()
@@ -86,10 +84,10 @@ function br.getOoCTime()
end
-- if getLowAllies(60) > 3 then
-function br.getLowAllies(Value)
+function combat:getLowAllies(Value)
local lowAllies = 0
- for i = 1, #br.friend do
- if br.friend[i].hp <= Value then
+ for i = 1, #br.engines.healingEngine.friend do
+ if br.engines.healingEngine.friend[i].hp <= Value then
lowAllies = lowAllies + 1
end
end
@@ -97,7 +95,7 @@ function br.getLowAllies(Value)
end
-- if getLowAlliesInTable(60, nNove) > 3 then
-function br.getLowAlliesInTable(Value, unitTable)
+function combat:getLowAlliesInTable(Value, unitTable)
local lowAllies = 0
for i = 1, #unitTable do
if unitTable[i].hp <= Value then
@@ -108,11 +106,16 @@ function br.getLowAlliesInTable(Value, unitTable)
end
-- if getNumEnemies("target",10) >= 3 then
-function br.getNumEnemies(Unit, Radius)
- return #br.getEnemies(Unit, Radius)
+function combat:getNumEnemies(Unit, Radius)
+ return #br.engines.enemiesEngineFunctions:getEnemies(Unit, Radius)
end
-function br.isIncapacitated(spellID)
+function combat:isIncapacitated(spellID)
+ -- Initialize tracking table if it doesn't exist
+ if not br.incapacitationTracker then
+ br.incapacitationTracker = {}
+ end
+
local event
local eventIndex = br._G.C_LossOfControl.GetActiveLossOfControlDataCount()
if eventIndex > 0 then
@@ -120,18 +123,51 @@ function br.isIncapacitated(spellID)
event = br._G.C_LossOfControl.GetActiveLossOfControlData(i)
if event then
local eventType = event.locType
- return not br.canRegainControl(spellID, eventType)
+ local canRegain = combat:canRegainControl(spellID, eventType)
+
+ -- If we cannot regain control (meaning we are incapacitated)
+ if not canRegain then
+ -- Create a unique key for this event type
+ local trackerKey = eventType .. "_" .. spellID
+
+ -- If this is a new incapacitation, record the start time
+ if not br.incapacitationTracker[trackerKey] then
+ br.incapacitationTracker[trackerKey] = br._G.GetTime()
+ end
+
+ -- Calculate time since incapacitation started
+ local timeSinceStart = br._G.GetTime() - br.incapacitationTracker[trackerKey]
+
+ -- Human-like delay: 0.15 to 0.45 seconds (base 0.3 +/- 0.15)
+ -- You can adjust this value or make it configurable
+ local reactionDelay = 0.15 + math.random() * 0.3
+
+ -- Only return true (we are incapacitated) if enough time has passed
+ if timeSinceStart >= reactionDelay then
+ return true
+ else
+ -- Still within reaction time, pretend we haven't noticed yet
+ return false
+ end
+ else
+ -- We can regain control, so clean up the tracker
+ local trackerKey = eventType .. "_" .. spellID
+ br.incapacitationTracker[trackerKey] = nil
+ end
end
end
+ else
+ -- No loss of control events, clear all trackers
+ br.incapacitationTracker = {}
end
return false
end
-function br.canRegainControl(spellID, controlEvent)
+function combat:canRegainControl(spellID, controlEvent)
local class = select(3, br._G.UnitClass("player"))
-- Warrior
if class == 1 then
- if spellID == 18499 and -- Fear, Sap and Incapacitate
+ if spellID == (br.functions.spell:getHighestSpellID("berserkerRage") or 18499) and -- Fear, Sap and Incapacitate
(controlEvent == "FEAR" or controlEvent == "FEAR_MECHANIC" or controlEvent == "ROOT"
or controlEvent == "SNARE" or controlEvent == "STUN" or controlEvent == "STUN_MECHANIC")
then
@@ -140,7 +176,7 @@ function br.canRegainControl(spellID, controlEvent)
end
-- Paladin
if class == 2 then
- if spellID == 1044 and (controlEvent == "ROOT" or controlEvent == "SNARE") then
+ if spellID == (br.functions.spell:getHighestSpellID("blessingOfFreedom") or 1044) and (controlEvent == "ROOT" or controlEvent == "SNARE") then
return true
end
end
@@ -161,12 +197,12 @@ function br.canRegainControl(spellID, controlEvent)
end
-- Death Knight
if class == 6 then
- if spellID == 49039 and --Lichborne
+ if spellID == (br.functions.spell:getHighestSpellID("lichborne") or 49039) and --Lichborne
(controlEvent == "CHARM" or controlEvent == "FEAR" or controlEvent == "FEAR_MECHANIC" or controlEvent == "SLEEP")
then
return true
end
- if spellID == 108201 and --Desecrated Ground
+ if spellID == (br.functions.spell:getHighestSpellID("desecratedGround") or 108201) and --Desecrated Ground
(controlEvent == "ROOT" or controlEvent == "SNARE")
then
return true
@@ -174,12 +210,12 @@ function br.canRegainControl(spellID, controlEvent)
end
-- Shaman
if class == 7 then
- if spellID == 58875 and -- Spirit Walk
+ if spellID == (br.functions.spell:getHighestSpellID("spiritWalk") or 58875) and -- Spirit Walk
(controlEvent == "ROOT" or controlEvent == "SNARE")
then
return true
end
- if spellID == 108273 and --Windwalk Totem
+ if spellID == (br.functions.spell:getHighestSpellID("windwalkTotem") or 108273) and --Windwalk Totem
(controlEvent == "ROOT" or controlEvent == "SNARE")
then
return true
@@ -199,7 +235,15 @@ function br.canRegainControl(spellID, controlEvent)
end
-- Monk
if class == 10 then
- if controlEvent == "ROOT" or controlEvent == "SNARE" then
+ if spellID ==(br.functions.spell:getHighestSpellID("tigersLust") or 116841) and -- Tiger's Lust
+ (controlEvent == "ROOT" or controlEvent == "SNARE")
+ then
+ return true
+ end
+ if spellID == (br.functions.spell:getHighestSpellID("nimbleBrew") or 137562) and -- Nimble Brew
+ (controlEvent == "ROOT" or controlEvent == "SNARE" or controlEvent == "STUN" or controlEvent == "STUN_MECHANIC"
+ or controlEvent == "FEAR" or controlEvent == "FEAR_MECHANIC")
+ then
return true
end
end
@@ -222,131 +266,235 @@ function br.canRegainControl(spellID, controlEvent)
end
-- if hasNoControl(12345) == true then
-function br.hasNoControl(spellID)
+function combat:hasNoControl(spellID)
local event
local eventIndex = br._G.C_LossOfControl.GetActiveLossOfControlDataCount()
if eventIndex > 0 then
for i = 0, eventIndex do
event = br._G.C_LossOfControl.GetActiveLossOfControlData(i)
if event then
- local regain = false
- if br.timer:useTimer("regainTimer", 0.25) then regain = br.canRegainControl(spellID, event.locType) end
+ local regain = combat:canRegainControl(spellID, event.locType)
+ if not regain and br.debug.timer:useTimer("regainTimer", 0.25) then regain = combat:canRegainControl(spellID, event.locType) end
return regain
- -- br._G.print("Has No Control: " ..
- -- tostring(event.locType) ..
- -- " - Can Regain Control: " ..
- -- tostring(br.canRegainControl(spellID, event.locType)) .. ", SpellID: " .. tostring(spellID))
- -- return br.canRegainControl(spellID, event.locType)
end
end
end
return false
end
--- if br.hasThreat("target") then
-function br.hasThreat(unit, playerUnit)
+-- Returns true if the player is under a "hard" loss-of-control effect (typically prevents casting/attacking).
+-- Also returns the matching event table (or nil).
+function combat:hasHardControl()
+ local hardTypes = {
+ STUN = true,
+ STUN_MECHANIC = true,
+ FEAR = true,
+ FEAR_MECHANIC = true,
+ CHARM = true,
+ SLEEP = true,
+ SILENCE = true,
+ PACIFY = true,
+ PACIFYSILENCE = true,
+ INCAPACITATE = true,
+ INCAPACITATE_MECHANIC = true,
+ DISORIENT = true,
+ HORROR = true,
+ }
+ local eventIndex = br._G.C_LossOfControl.GetActiveLossOfControlDataCount()
+ if eventIndex > 0 then
+ for i = 0, eventIndex do
+ local event = br._G.C_LossOfControl.GetActiveLossOfControlData(i)
+ if event and event.locType and hardTypes[event.locType] then
+ return true, event
+ end
+ end
+ end
+ return false, nil
+end
+
+-- Returns true if the player cannot cast *this* spell due to hard loss of control.
+-- Allows "regain control" spells (e.g., freedom/berserker rage) via canRegainControl().
+function combat:cannotCast(spellID)
+ local hard, event = combat:hasHardControl()
+ if not hard or not event or not event.locType then
+ return false
+ end
+ if spellID ~= nil and combat:canRegainControl(spellID, event.locType) then
+ return false
+ end
+ return true
+end
+
+local threatCache = {}
+-- if br.functions.combat:hasThreat("target") then
+function combat:hasThreat(unit, playerUnit)
-- Early Exit
if unit == nil then return false end
-- Dummy Validation
- if br.isDummy(unit) and br.getDistance("target", unit) < 8 then return true end
+ if br.functions.unit:isDummy(unit) and br.functions.range:getDistance("target", unit) < 8 then return true end
-- Damaged Validation
- if br.damaged[br._G.ObjectPointer(unit)] ~= nil then
- if br.isChecked("Threat Debug") then --and not br.GetUnitExists("target") then
+ if br.engines.enemiesEngine.damaged[br._G.ObjectPointer(unit)] ~= nil then
+ if br.functions.misc:isChecked("Threat Debug") then --and not br.functions.unit:GetUnitExists("target") then
br._G.print("[Damage Threat] " .. br._G.UnitName(unit) .. " was attacked, it now hates you.")
end
return true
end
+
+ -- FIXED: Early exit for already validated enemies to avoid expensive checks
+ -- Safety check: Still verify unit is attackable to handle evade/reset/faction change scenarios
+ local unitPointer = br._G.ObjectPointer(unit)
+ if br._G.UnitAffectingCombat("player") and br.engines.enemiesEngine.enemy[unitPointer] ~= nil
+ and br._G.UnitCanAttack("player", unit) and not br.functions.unit:GetUnitIsDeadOrGhost(unit)
+ then
+ if br.functions.misc:isChecked("Threat Debug") then
+ br._G.print("[Validated Enemy Threat] " .. br._G.UnitName(unit) .. " is already in enemy table.")
+ end
+ return true
+ end
+
+ -- Per-unit cache for expensive threat computation (0.1s TTL)
+ local _tcNow = br._G.GetTime()
+ local _tcEntry = threatCache[unit]
+ if _tcEntry and (_tcNow - _tcEntry.time) < 0.1 then return _tcEntry.value end
+
if playerUnit == nil then
playerUnit = "player"
end
local targetUnit, targetFriend
- if br.GetUnit(unit) == nil or br.GetUnitIsDeadOrGhost(unit) or br._G.UnitIsTapDenied(unit) then
+ if br.functions.unit:GetUnit(unit) == nil or br.functions.unit:GetUnitIsDeadOrGhost(unit) or br._G.UnitIsTapDenied(unit) then
targetUnit = "None"
- elseif br._G.UnitTarget(br.GetUnit(unit)) ~= nil then
- targetUnit = br._G.UnitTarget(br.GetUnit(unit))
+ elseif br._G.UnitTarget(br.functions.unit:GetUnit(unit)) ~= nil then
+ targetUnit = br._G.UnitTarget(br.functions.unit:GetUnit(unit))
else
targetUnit = "None"
end
if not targetUnit or targetUnit == "None" then
targetFriend = false
else
- targetFriend = (br.isTargeting(unit) or br._G.UnitName(targetUnit) == br._G.UnitName("player")
- or (br.GetUnitExists("pet") and br._G.UnitName(targetUnit) == br._G.UnitName("pet"))
+ targetFriend = (br.functions.misc:isTargeting(unit) or br._G.UnitName(targetUnit) == br._G.UnitName("player")
+ or (br.functions.unit:GetUnitExists("pet") and br._G.UnitName(targetUnit) == br._G.UnitName("pet"))
or br._G.UnitInParty(targetUnit) or br._G.UnitInRaid(targetUnit))
end
local function threatSituation(friendlyUnit, enemyUnit)
- local friendlyUnit = br._G.UnitGUID(friendlyUnit)
if not br._G.UnitIsVisible(friendlyUnit) or not br._G.UnitIsVisible(enemyUnit) then
return false
end
local status = br._G.UnitThreatSituation(friendlyUnit, enemyUnit)
+ -- Also check detailed threat situation as a fallback
+ if status == nil then
+ local _, _, _, _, threatPercent = br._G.UnitDetailedThreatSituation(friendlyUnit, enemyUnit)
+ -- If we have any threat percentage, consider it a valid threat situation
+ if threatPercent and threatPercent > 0 then
+ if br.functions.misc:isChecked("Threat Debug") then
+ br._G.print("[Detailed Threat] " .. br._G.UnitName(friendlyUnit) .. " has " ..
+ br.functions.misc:round2(threatPercent, 1) .. "% threat on " .. br._G.UnitName(enemyUnit))
+ end
+ return true
+ end
+ end
return status ~= nil
end
- -- Valididation Checks
+ -- Validation Checks
-- Print("Unit: "..tostring(UnitName(unit)).." | Player: "..tostring(playerUnit))
local playerInCombat = br._G.UnitAffectingCombat("player")
local unitInCombat = br._G.UnitAffectingCombat(unit)
- local unitObject = br._G.ObjectPointer(unit)
+ -- local unitObject = br._G.ObjectPointer(unit)
local instance = select(2, br._G.IsInInstance())
+
+ -- Tapped Unit Targeting Validation
+ -- Check if tapped unit is targeting player or group (handles quest mobs that turn hostile)
+ if br._G.UnitIsTapDenied(unit) and targetFriend then
+ if br.functions.misc:isChecked("Threat Debug") then
+ br._G.print("[Tapped Targeting Threat] " ..
+ br._G.UnitName(unit) .. " is tapped but targeting " .. br._G.UnitName(targetUnit))
+ end
+ return true
+ end
+
-- Unit is Targeting Player/Pet/Party/Raid Validation
if targetFriend then
- if br.isChecked("Threat Debug") then --and not br.GetObjectExists("target") then
+ if br.functions.misc:isChecked("Threat Debug") and not br._G.UnitIsUnit("target", unit) then --and not br.functions.unit:GetObjectExists("target") then
br._G.print("[Targeting Threat] " ..
- br._G.UnitName(br.GetUnit(unit)) .. " is targeting " .. br._G.UnitName(targetUnit))
+ br._G.UnitName(br.functions.unit:GetUnit(unit)) .. " is targeting " .. br._G.UnitName(targetUnit))
end
return true
end
-- Boss Adds Validation
- if br.isBoss(unit) and unitInCombat and (instance == "party" or instance == "raid") then
- if br.isChecked("Threat Debug") then
+ if br.functions.unit:isBoss(unit) and unitInCombat and (instance == "party" or instance == "raid") then
+ if br.functions.misc:isChecked("Threat Debug") then
br._G.print("[Boss Threat] " .. br._G.UnitName(unit) ..
" has threat with you.")
end
return true
-- Threat Bypass Validation
- -- elseif playerInCombat and br.lists.threatBypass[unitID] ~= nil and #br.getEnemies(unit,20,true) == 0 then
+ -- elseif playerInCombat and br.lists.threatBypass[unitID] ~= nil and #br.engines.enemiesEngineFunctions:getEnemies(unit,20,true) == 0 then
-- return true
end
-- Open World Mob Pack Validation
- if instance == "none" and playerInCombat and not br.isCritter(unitObject) and br.enemy[br._G.ObjectPointer("target")] ~= nil and br.enemy[unitObject] == nil and br.getDistance("target", unitObject) < 8 then
- -- if br.isChecked("Cast Debug") then br._G.print("[Open World Threat] "..br._G.UnitName(unit).." is within "..round2(getDistance("target",unitObject),1).."yrds of your target and is considered a threat.") end
- return true
+ if instance == "none" and playerInCombat and not br.functions.unit:isCritter(unit) then
+ local unitPointer = br._G.ObjectPointer(unit)
+ local targetPointer = br._G.ObjectPointer("target")
+
+ -- If unit is already in the enemy engine, it has threat
+ if br.engines.enemiesEngine.enemy[unitPointer] ~= nil then
+ if br.functions.misc:isChecked("Threat Debug") then
+ br._G.print("[Open World Threat] " .. br._G.UnitName(unit) .. " is in enemy engine and has threat.")
+ end
+ return true
+ end
+
+ -- If unit is near your current target, it's part of the pack
+ if br.engines.enemiesEngine.enemy[targetPointer] ~= nil then
+ local distanceToTarget = br.functions.range:getDistance("target", unit)
+ if distanceToTarget < 8 then
+ if br.functions.misc:isChecked("Threat Debug") then
+ br._G.print("[Open World Threat] " .. br._G.UnitName(unit) .. " is within " ..
+ br.functions.misc:round2(distanceToTarget, 1) .. "yrds of your target and is considered a threat.")
+ end
+ return true
+ end
+ end
end
- -- Player Threat Valdation
- if threatSituation(playerUnit, unit) then
- if br.isChecked("Threat Debug") then --and not br.GetObjectExists("target") then
+ -- Player Threat Validation
+ if not br.functions.unit:GetUnitIsDeadOrGhost(unit) and threatSituation(playerUnit, unit) then
+ if br.functions.misc:isChecked("Threat Debug") then --and not br.functions.unit:GetObjectExists("target") then
br._G.print("[Player Threat] " .. br._G.UnitName(playerUnit) .. " has threat with " .. br._G.UnitName(unit))
end
+ threatCache[unit] = { value = true, time = _tcNow }
return true
end
-- Party/Raid Threat Validation
- if #br.friend > 1 then
- for i = 1, #br.friend do
- if br.friend[i] then
- local thisUnit = br.friend[i].unit
+ if not br.functions.unit:GetUnitIsDeadOrGhost(unit) and #br.engines.healingEngine.friend >= 1 then
+ for i = 1, #br.engines.healingEngine.friend do
+ if br.engines.healingEngine.friend[i] then
+ local thisUnit = br.engines.healingEngine.friend[i].unit
if threatSituation(thisUnit, unit) then
- if br.isChecked("Threat Debug") then --and not br.GetObjectExists("target") then
+ if br.functions.misc:isChecked("Threat Debug") then --and not br.functions.unit:GetObjectExists("target") then
br._G.print("[Party/Raid Threat] " ..
br._G.UnitName(thisUnit) .. " has threat with " .. br._G.UnitName(unit))
end
+ threatCache[unit] = { value = true, time = _tcNow }
return true
end
end
end
end
+ -- Note: Fallback removed - the above checks should be sufficient to detect valid threats
+ -- without risking false positives from mobs fighting NPCs or other groups
+ threatCache[unit] = { value = false, time = _tcNow }
return false
end
-function br.isTanking(unit)
+function combat:isTanking(unit)
return br._G.UnitThreatSituation("player", unit) ~= nil and br._G.UnitThreatSituation("player", unit) >= 2
end
-- if isAggroed("target") then
-function br.isAggroed(unit)
- for i = 1, #br.friend do
- local friend = br.friend[i].unit
+function combat:isAggroed(unit)
+ for i = 1, #br.engines.healingEngine.friend do
+ local friend = br.engines.healingEngine.friend[i].unit
local threat = select(5, br._G.UnitDetailedThreatSituation(friend, unit))
if threat ~= nil then
if threat >= 0 then
diff --git a/System/Functions/Custom.lua b/System/Functions/Custom.lua
index 0eb8d6e4c3..811d2bd833 100644
--- a/System/Functions/Custom.lua
+++ b/System/Functions/Custom.lua
@@ -1,28 +1,32 @@
local _, br = ...
+br.functions.custom = br.functions.custom or {}
+local custom = br.functions.custom
+
-- Functions from coders for public use
local sqrt, cos, sin = math.sqrt, math.cos, math.sin
--[[ ]]
--[[ ragnar ]]
--[[ ]]
-function br.unitLookup(Unit, returnType)
- for k, _ in pairs(br.enemy) do
- if br.enemy[k].guid == Unit or br.enemy[k].unit == Unit then
+function custom:unitLookup(Unit, returnType)
+ for k, _ in pairs(br.engines.enemiesEngine.enemy) do
+ local enemy = br.engines.enemiesEngine.enemy[k]
+ if enemy.guid == Unit or enemy.unit == Unit then
if returnType == "guid" then
- return br.enemy[k].guid
+ return enemy.guid
elseif returnType == "table" then
- return br.enemy[k]
+ return enemy
else
- return br.enemy[k].unit
+ return enemy.unit
end
end
end
end
-function br.getUnitCount(ID, maxRange, tapped)
+function custom:getUnitCount(ID, maxRange, tapped)
local counter = 0
- for _, enemy in pairs(br.enemy) do
+ for _, enemy in pairs(br.engines.enemiesEngine.enemy) do
local thisUnit = enemy.unit
- if enemy.id == ID and br.getDistance(thisUnit) < maxRange then
+ if enemy.id == ID and br.functions.range:getDistance(thisUnit) < maxRange then
local unitTapped = not br._G.UnitIsTapDenied(thisUnit)
if (tapped == true and unitTapped) or not tapped then
counter = counter + 1
@@ -32,47 +36,23 @@ function br.getUnitCount(ID, maxRange, tapped)
return counter
end
-function br.isCCed(Unit)
+function custom:isCCed(Unit)
local CCTable = { 84868, 3355, 19386, 118, 28272, 28271, 61305, 61721, 161372, 61780, 161355, 126819, 161354, 115078,
20066, 9484, 6770, 1776, 51514, 107079, 10326, 8122, 154359, 2094, 5246, 5782, 5484, 6358, 115268, 339 };
for i = 1, #CCTable do
- if br.UnitDebuffID(Unit, CCTable[i]) then
+ if br.functions.aura:UnitDebuffID(Unit, CCTable[i]) then
return true
end
end
return false
end
---cast spell on position x,y,z
-function br.castAtPosition(X, Y, Z, SpellID)
- local i = -100
- local mouselookActive = false
- if br._G.IsMouselooking() then
- mouselookActive = true
- br._G.MouselookStop()
- end
- br._G.CastSpellByName(br._G.GetSpellInfo(SpellID), "player")
- while br._G["IsAoEPending"]() and i <= 100 do
- br._G["ClickPosition"](X, Y, Z)
- br.castPosition.x = X
- br.castPosition.y = Y
- br.castPosition.z = Z
- Z = i
- i = i + 1
- end
- if mouselookActive then
- br._G.MouselookStart()
- end
- if i >= 100 and br._G["IsAoEPending"]() then return false end
- return true
-end
-
--get number of units around 1 unit
-function br.getUnits(thisUnit, allUnitsInRange, radius)
+function custom:getUnits(thisUnit, allUnitsInRange, radius)
local unitsAroundThisUnit = {}
for j = 1, #allUnitsInRange do
local checkUnit = allUnitsInRange[j]
- if br.getDistance(thisUnit, checkUnit) < radius then
+ if br.functions.range:getDistance(thisUnit, checkUnit) < radius then
table.insert(unitsAroundThisUnit, checkUnit)
end
end
@@ -80,28 +60,28 @@ function br.getUnits(thisUnit, allUnitsInRange, radius)
end
-- check if unit is blacklisted
-function br.isNotBlacklisted(checkUnit)
+function custom:isNotBlacklisted(checkUnit)
local blacklistUnitID = {}
if checkUnit == nil then return false end
for i = 1, #blacklistUnitID do
- if br.GetObjectID(checkUnit) == blacklistUnitID[i] then return false end
+ if br.functions.unit:GetObjectID(checkUnit) == blacklistUnitID[i] then return false end
end
return true
end
-function br.castGroundAtUnit(spellID, radius, minUnits, maxRange, minRange, spellType, unit)
- local _, _, _, _, spellMinRange, spellMaxRange = br._G.GetSpellInfo(spellID)
+function custom:castGroundAtUnit(spellID, radius, minUnits, maxRange, minRange, spellType, unit)
+ local _, _, _, _, spellMinRange, spellMaxRange = br.api.wow.GetSpellInfo(spellID)
minRange = minRange or spellMinRange or 0
maxRange = maxRange or spellMaxRange or 5
radius = radius or maxRange
local allUnitsInRange
if spellType == "heal" then
- allUnitsInRange = br.getAllies("player", 40)
+ allUnitsInRange = br.engines.healingEngineFunctions:getAllies("player", 40)
else
allUnitsInRange = {}
- local maxEnemies = br.getEnemies("player", maxRange, true)
- local minEnemies = br.getEnemies("player", minRange, true)
+ local maxEnemies = br.engines.enemiesEngineFunctions:getEnemies("player", maxRange, true)
+ local minEnemies = br.engines.enemiesEngineFunctions:getEnemies("player", minRange, true)
for _, maxUnit in pairs(maxEnemies) do
local minUnitInMaxEnemies = false
for _, minUnit in pairs(minEnemies) do
@@ -116,17 +96,17 @@ function br.castGroundAtUnit(spellID, radius, minUnits, maxRange, minRange, spel
end
end
- local enemiesInRadius = #br.getEnemies(unit, radius)
- local visibleEnemiesInRadius = #br.getEnemies(unit, radius, true)
+ local enemiesInRadius = #br.engines.enemiesEngineFunctions:getEnemies(unit, radius)
+ local visibleEnemiesInRadius = #br.engines.enemiesEngineFunctions:getEnemies(unit, radius, true)
- if br.getUnits(unit, allUnitsInRange, radius - 3) >= minUnits and enemiesInRadius >= visibleEnemiesInRadius then
- local X1, Y1, Z1 = br.GetObjectPosition(unit)
- return br.castAtPosition(X1, Y1, Z1, spellID)
+ if br.functions.custom:getUnits(unit, allUnitsInRange, radius - 3) >= minUnits and enemiesInRadius >= visibleEnemiesInRadius then
+ local X1, Y1, Z1 = br.functions.unit:GetObjectPosition(unit)
+ return br.functions.cast:castAtPosition(X1, Y1, Z1, spellID)
end
end
--- function br.castGroundAtBestLocation(spellID, radius, minUnits, maxRange, minRange, spellType, castTime)
--- local _, _, _, _, spellMinRange, spellMaxRange = br._G.GetSpellInfo(spellID)
+-- function custom:castGroundAtBestLocation(spellID, radius, minUnits, maxRange, minRange, spellType, castTime)
+-- local _, _, _, _, spellMinRange, spellMaxRange = br.api.wow.GetSpellInfo(spellID)
-- minRange = minRange or spellMinRange or 0
-- maxRange = maxRange or spellMaxRange or 5
-- radius = radius or maxRange
@@ -169,9 +149,9 @@ end
-- local function unitInCircle(unit, cx, cy)
-- local uX, uY
-- if castTime == nil or castTime == 0 then
--- uX, uY = br.GetObjectPosition(unit)
+-- uX, uY = br.functions.unit:GetObjectPosition(unit)
-- else
--- uX, uY = br.GetFuturePostion(unit, castTime)
+-- uX, uY = br.functions.custom:(unit, castTime)
-- end
-- local rUnit = br._G["UnitBoundingRadius"](unit)
-- return math.abs((uX - cx) * (uX - cx) + (uY - cy) * (uY - cy)) <= (rUnit + radius) * (rUnit + radius);
@@ -181,9 +161,9 @@ end
-- local function unitDistanceCenter(unit, cx, cy)
-- local uX, uY
-- if castTime == nil or castTime == 0 then
--- uX, uY = br.GetObjectPosition(unit)
+-- uX, uY = br.functions.unit:GetObjectPosition(unit)
-- else
--- uX, uY = br.GetFuturePostion(unit, castTime)
+-- uX, uY = br.functions.custom:(unit, castTime)
-- end
-- -- local rUnit = br._G.UnitBoundingRadius(unit)
-- return sqrt(((uX-cx)^2) + ((uY-cy)^2))
@@ -192,9 +172,9 @@ end
-- if minRange == nil then minRange = 0 end
-- local allUnitsInRange
-- if spellType == "heal" then
--- allUnitsInRange = br.getAllies("player",maxRange)
+-- allUnitsInRange = br.engines.healingEngineFunctions:getAllies("player",maxRange)
-- else
--- allUnitsInRange = br.getEnemies("player",maxRange,false)
+-- allUnitsInRange = br.engines.enemiesEngineFunctions:getEnemies("player",maxRange,false)
-- end
-- local testCircles = {}
@@ -202,10 +182,10 @@ end
-- local combs = getAllCombinationsOfASet(allUnitsInRange, 2)
-- for _, val in pairs(combs) do
-- local temp = {}
--- local tX1, tY1, tZ1 = (castTime == nil or castTime == 0) and br.GetObjectPosition(val[1]) or 0,0,0
--- or br.GetFuturePostion(val[1], castTime)
--- local tX2, tY2, tZ2 = (castTime == nil or castTime == 0) and br.GetObjectPosition(val[2]) or 0,0,0
--- or br.GetFuturePostion(val[2], castTime)
+-- local tX1, tY1, tZ1 = (castTime == nil or castTime == 0) and br.functions.unit:GetObjectPosition(val[1]) or 0,0,0
+-- or br.functions.custom:(val[1], castTime)
+-- local tX2, tY2, tZ2 = (castTime == nil or castTime == 0) and br.functions.unit:GetObjectPosition(val[2]) or 0,0,0
+-- or br.functions.custom:(val[2], castTime)
-- --distance
-- local q = sqrt((tX2-tX1)^2 + (tY2-tY1)^2)
@@ -233,8 +213,8 @@ end
-- local bestCircle = {x = 0, y = 0, z = 0, q = 0, nro = 0}
-- for i=1, #testCircles do
-- local thisCircle = testCircles[i]
--- if br.getDistanceToLocation("player",thisCircle.xfc,thisCircle.yfc,thisCircle.z) > minRange
--- or br.getDistanceToLocation("player",thisCircle.xsc,thisCircle.ysc,thisCircle.z) > minRange
+-- if br.functions.range:getDistanceToLocation("player",thisCircle.xfc,thisCircle.yfc,thisCircle.z) > minRange
+-- or br.functions.range:getDistanceToLocation("player",thisCircle.xsc,thisCircle.ysc,thisCircle.z) > minRange
-- then
-- local tempData = { {count=0, units={}, x=thisCircle.xfc, y=thisCircle.yfc},
-- {count=0, units={}, x=thisCircle.xsc, y=thisCircle.ysc} }
@@ -272,8 +252,8 @@ end
-- -- end
-- --check with minUnits
--- if minUnits == 1 and bestCircle.nro == 0 and br.GetUnitExists("target") and br.getDistance("player","target") > minRange then
--- if br.castGround("target",spellID,maxRange,minRange,radius,castTime) then return true else return false end
+-- if minUnits == 1 and bestCircle.nro == 0 and br.functions.unit:GetUnitExists("target") and br.functions.range:getDistance("player","target") > minRange then
+-- if br.functions.cast:castGround("target",spellID,maxRange,minRange,radius,castTime) then return true else return false end
-- end
-- if bestCircle.nro < minUnits then return false end
@@ -294,101 +274,185 @@ end
-- end
-- end
-- bestCircle.x, bestCircle.y = (newBestCircleX + math.random() * 2), (newBestCircleY + math.random() * 2)
--- if br.castAtPosition(bestCircle.x,bestCircle.y,bestCircle.z, spellID) then return true else return false end
+-- if br.functions.cast:castAtPosition(bestCircle.x,bestCircle.y,bestCircle.z, spellID) then return true else return false end
-- end
-- end
-function br.castGroundAtBestLocation(spellID, radius, minUnits, maxRange, minRange, spellType, castTime)
- local allUnitsInRange = (spellType == "heal") and br.getAllies("player", maxRange) or
- br.getEnemies("player", maxRange, false)
+function custom:castGroundAtBestLocation(spellID, radius, minUnits, maxRange, minRange, spellType, castTime)
+ -- Finds the best ground location (within spell range) that maximizes the number of units inside a
+ -- fixed-radius circle. If the maximum hit count is >= minUnits, casts at that location.
+ --
+ -- NOTE: castTime is currently unused; reliable movement prediction is not available here.
- local bestLocation = nil
- local maxHitCount = 0
- local playerX, playerY, playerZ = br.GetObjectPosition("player")
+ local _, _, _, _, spellMinRange, spellMaxRange = br.api.wow.GetSpellInfo(spellID)
+ minRange = minRange or spellMinRange or 0
+ maxRange = maxRange or spellMaxRange or 5
+ radius = radius or maxRange
+ minUnits = minUnits or 1
- local function GetUnitMovementDirectionAndSpeed(unit)
- -- Placeholder function: In practice, this would require complex calculations
- -- and might not be entirely accurate due to unpredictable player behavior.
- local direction = 0 -- Direction the unit is moving in radians
- local speed = 0 -- Speed of the unit
+ local allUnitsInRange = (spellType == "heal")
+ and br.engines.healingEngineFunctions:getAllies("player", maxRange)
+ or br.engines.enemiesEngineFunctions:getEnemies("player", maxRange, false)
- -- You would need to calculate the actual direction and speed based on the unit's movement.
- -- World of Warcraft's API may not provide direct methods to get these values accurately.
+ if not allUnitsInRange or #allUnitsInRange == 0 then return false end
- return direction, speed
+ local playerX, playerY, playerZ = br.functions.unit:GetObjectPosition("player")
+
+ local function dist2(x1, y1, x2, y2)
+ local dx, dy = x2 - x1, y2 - y1
+ return dx * dx + dy * dy
end
- local function GetFuturePosition(unit, castTime)
- -- Get the current position of the unit
- local currentX, currentY, currentZ = br.GetObjectPosition(unit)
+ local function inSpellRange(cx, cy, cz)
+ local d = math.sqrt(dist2(playerX, playerY, cx, cy))
+ return d <= maxRange and d >= (minRange or 0)
+ end
- -- Estimate the unit's current movement direction and speed
- -- This is a simplified example. In practice, this can be quite complex.
- local direction, speed = GetUnitMovementDirectionAndSpeed(unit)
+ -- Build a compact list of points we can evaluate.
+ local points = {}
+ for i = 1, #allUnitsInRange do
+ local unitObj = allUnitsInRange[i]
+ local unitToken = (spellType == "heal" and unitObj.unit) or unitObj
+ if unitToken then
+ local x, y, z = br.functions.unit:GetObjectPosition(unitToken)
+ if x and y and z then
+ points[#points + 1] = { unit = unitToken, x = x, y = y, z = z }
+ end
+ end
+ end
- -- Calculate the future position based on current position, direction, speed, and castTime
- local futureX = currentX + speed * castTime * math.cos(direction)
- local futureY = currentY + speed * castTime * math.sin(direction)
- local futureZ = currentZ -- Assuming no vertical movement for simplicity
+ if #points == 0 then return false end
- return futureX, futureY, futureZ
- end
+ local candidates = {}
- local function IsWithinRadiusFuture(center, target, radius, castTime)
- local futureCenterX, futureCenterY = GetFuturePosition(center, castTime)
- local futureTargetX, futureTargetY = GetFuturePosition(target, castTime)
- local dx, dy = futureCenterX - futureTargetX, futureCenterY - futureTargetY
- return (dx * dx + dy * dy) <= (radius * radius)
+ -- Candidate centers: every unit position.
+ for i = 1, #points do
+ local p = points[i]
+ candidates[#candidates + 1] = { x = p.x, y = p.y, z = p.z }
end
- local function CalculateFutureClusterCenter(cluster, castTime)
+ -- Candidate centers: circle intersection points for each pair within 2*radius.
+ -- Standard algorithm: for two points p1, p2 with distance d <= 2r,
+ -- compute midpoint m and perpendicular offset h to get two possible centers.
+ local r = radius
+ local r2 = r * r
+ local maxPairDist2 = (2 * r) * (2 * r)
+
+ local function getHitInfoAt(cx, cy, cz)
+ local count = 0
local sumX, sumY, sumZ = 0, 0, 0
- for _, unit in ipairs(cluster) do
- local x, y, z = GetFuturePosition(unit, castTime)
- sumX = sumX + x
- sumY = sumY + y
- sumZ = sumZ + z
+ for j = 1, #points do
+ local p = points[j]
+ if dist2(cx, cy, p.x, p.y) <= r2 then
+ count = count + 1
+ sumX = sumX + p.x
+ sumY = sumY + p.y
+ sumZ = sumZ + p.z
+ end
+ end
+ if count == 0 then
+ return 0, nil, nil
+ end
+ local centroid = { x = sumX / count, y = sumY / count, z = sumZ / count }
+
+ -- Centering score: minimize the maximum distance from centroid to any covered unit.
+ local maxDistToCentroid2 = 0
+ for j = 1, #points do
+ local p = points[j]
+ if dist2(cx, cy, p.x, p.y) <= r2 then
+ local d2 = dist2(centroid.x, centroid.y, p.x, p.y)
+ if d2 > maxDistToCentroid2 then
+ maxDistToCentroid2 = d2
+ end
+ end
end
- return {
- x = sumX / #cluster,
- y = sumY / #cluster,
- z = sumZ / #cluster
- }
- end
- local function GetDistance(x1, y1, x2, y2)
- return math.sqrt((x2 - x1) ^ 2 + (y2 - y1) ^ 2)
+ return count, centroid, maxDistToCentroid2
end
-
- for _, potentialCenter in ipairs(allUnitsInRange) do
- local hitCount = 0
- local cluster = {}
-
- for _, target in ipairs(allUnitsInRange) do
- if IsWithinRadiusFuture(potentialCenter, target, radius, castTime) then
- hitCount = hitCount + 1
- table.insert(cluster, target)
+ for i = 1, #points - 1 do
+ local p1 = points[i]
+ for j = i + 1, #points do
+ local p2 = points[j]
+ local dSq = dist2(p1.x, p1.y, p2.x, p2.y)
+ if dSq > 0 and dSq <= maxPairDist2 then
+ local d = math.sqrt(dSq)
+ local mx, my = (p1.x + p2.x) / 2, (p1.y + p2.y) / 2
+ local hSq = r2 - (d * d) / 4
+ if hSq >= 0 then
+ local h = math.sqrt(hSq)
+ local ux, uy = (p2.x - p1.x) / d, (p2.y - p1.y) / d
+ local px, py = -uy, ux
+ local z = (p1.z + p2.z) / 2
+ candidates[#candidates + 1] = { x = mx + px * h, y = my + py * h, z = z }
+ candidates[#candidates + 1] = { x = mx - px * h, y = my - py * h, z = z }
+ end
end
end
+ end
- local clusterCenter = CalculateFutureClusterCenter(cluster, castTime)
- local distanceFromPlayer = GetDistance(playerX, playerY, clusterCenter.x, clusterCenter.y)
+ local bestLocation = nil
+ local bestCentroid = nil
+ local bestCount = 0
+ local bestMaxDistToCentroid2 = nil
+ local bestDistToPlayer2 = nil
+
+ for i = 1, #candidates do
+ local c = candidates[i]
+ if inSpellRange(c.x, c.y, c.z) then
+ local count, centroid, maxDistToCentroid2 = getHitInfoAt(c.x, c.y, c.z)
+
+ if count > bestCount then
+ bestCount = count
+ bestLocation = c
+ bestCentroid = centroid
+ bestMaxDistToCentroid2 = maxDistToCentroid2
+ bestDistToPlayer2 = dist2(playerX, playerY, c.x, c.y)
+ elseif count == bestCount and bestCount > 0 then
+ -- Tie-breaker order:
+ -- 1) More centered (smaller max distance to centroid)
+ -- 2) Closer to player (minor preference)
+ if centroid and maxDistToCentroid2 then
+ if (not bestMaxDistToCentroid2) or (maxDistToCentroid2 < bestMaxDistToCentroid2) then
+ bestLocation = c
+ bestCentroid = centroid
+ bestMaxDistToCentroid2 = maxDistToCentroid2
+ bestDistToPlayer2 = dist2(playerX, playerY, c.x, c.y)
+ elseif maxDistToCentroid2 == bestMaxDistToCentroid2 then
+ local d2 = dist2(playerX, playerY, c.x, c.y)
+ if not bestDistToPlayer2 or d2 < bestDistToPlayer2 then
+ bestLocation = c
+ bestCentroid = centroid
+ bestDistToPlayer2 = d2
+ end
+ end
+ end
+ end
+ end
+ end
- if hitCount >= minUnits and hitCount > maxHitCount and distanceFromPlayer >= minRange then
- bestLocation = clusterCenter
- maxHitCount = hitCount
+ -- If we found a best cluster, try snapping the cast to the centroid of the covered group.
+ -- This keeps the same hit count when possible, and avoids placing the pack on the edge.
+ if bestCentroid and inSpellRange(bestCentroid.x, bestCentroid.y, bestCentroid.z) then
+ local centroidCount = 0
+ for j = 1, #points do
+ local p = points[j]
+ if dist2(bestCentroid.x, bestCentroid.y, p.x, p.y) <= r2 then
+ centroidCount = centroidCount + 1
+ end
+ end
+ if centroidCount == bestCount then
+ bestLocation = bestCentroid
end
end
- if bestLocation and maxHitCount >= minUnits then
- -- Cast the spell at the bestLocation
- return br.castAtPosition(bestLocation.x, bestLocation.y, bestLocation.z, spellID)
+ if bestLocation and bestCount >= minUnits then
+ return br.functions.cast:castAtPosition(bestLocation.x, bestLocation.y, bestLocation.z, spellID)
end
return false
end
-function br.isUnitThere(unitNameOrID, distance)
+function custom:isUnitThere(unitNameOrID, distance)
-- description:
-- check if Unit with ID or name is around
@@ -399,20 +463,20 @@ function br.isUnitThere(unitNameOrID, distance)
-- isUnitThere("Shadowfel Warden")
if type(unitNameOrID) == "number" then
- for k, _ in pairs(br.enemy) do
- local thisUnit = br.enemy[k].unit
- if br.GetObjectID(thisUnit) then
- if distance == nil or br.getDistance("player", thisUnit) < distance then
+ for k, _ in pairs(br.engines.enemiesEngine.enemy) do
+ local thisUnit = br.engines.enemiesEngine.enemy[k].unit
+ if br.functions.unit:GetObjectID(thisUnit) then
+ if distance == nil or br.functions.range:getDistance("player", thisUnit) < distance then
return true
end
end
end
end
if type(unitNameOrID) == "string" then
- for k, _ in pairs(br.enemy) do
- local thisUnit = br.enemy[k].unit
+ for k, _ in pairs(br.engines.enemiesEngine.enemy) do
+ local thisUnit = br.engines.enemiesEngine.enemy[k].unit
if br._G.UnitName(thisUnit) == unitNameOrID then
- if distance == nil or br.getDistance("player", thisUnit) < distance then
+ if distance == nil or br.functions.range:getDistance("player", thisUnit) < distance then
return true
end
end
@@ -420,7 +484,7 @@ function br.isUnitThere(unitNameOrID, distance)
end
end
-function br.getTooltipSize(SpellID)
+function custom:getTooltipSize(SpellID)
-- description
-- get the dmg or heal value from a tooltip
@@ -434,19 +498,19 @@ function br.getTooltipSize(SpellID)
return tonumber(n1 .. n2)
end
-function br.castBossButton(target)
+function custom:castBossButton(target)
if target == nil then
br._G.RunMacroText("/click ExtraActionButton1")
return true
else
- br._G.TargetUnit(target)
+ -- br._G.TargetUnit(target)
br._G.RunMacroText("/click ExtraActionButton1")
return true
end
end
-- get threat situation on player and return the number
-function br.getThreat()
+function custom:getThreat()
if br._G.UnitThreatSituation("player") ~= nil then
return br._G.UnitThreatSituation("player")
end
@@ -457,7 +521,7 @@ function br.getThreat()
return 0
end
-function br.RaidBuff(BuffSlot, myBuffSpellID)
+function custom:RaidBuff(BuffSlot, myBuffSpellID)
-- description:
-- check for raidbuff and cast if missing
@@ -522,7 +586,7 @@ function br.RaidBuff(BuffSlot, myBuffSpellID)
if br._G.GetNumGroupMembers() == 0 then
if not br._G.UnitIsDeadOrGhost("player") then
if not br._G.GetRaidBuffTrayAuraInfo(id) then
- if br.castSpell("player", SpellID) then return true end
+ if br.functions.cast:castSpell("player", SpellID) then return true end
end
end
else
@@ -531,12 +595,12 @@ function br.RaidBuff(BuffSlot, myBuffSpellID)
else
for index = 1, br._G.GetNumGroupMembers() do
local _, _, _, _, _, _, _, online, isDead, _, _ = br._G.GetRaidRosterInfo(index)
- if online and not isDead and 1 == br._G.C_Spell.IsSpellInRange(select(1, br._G.GetSpellInfo(SpellID)), "raid" .. index) then
+ if online and not isDead and br._G.C_Spell.IsSpellInRange(select(1, br.api.wow.GetSpellInfo(SpellID)), "raid" .. index) == true then
-- local playerBuffed=false
for auraIndex = 1, #chosenTable do
- if br.getBuffRemain("raid" .. index, chosenTable[auraIndex]) > 0 then break end
- if br.getBuffRemain("raid" .. index, chosenTable[auraIndex]) <= 0 then
- if br.castSpell("player", SpellID, true, false) then return true end
+ if br.functions.aura:getBuffRemain("raid" .. index, chosenTable[auraIndex]) > 0 then break end
+ if br.functions.aura:getBuffRemain("raid" .. index, chosenTable[auraIndex]) <= 0 then
+ if br.functions.cast:castSpell("player", SpellID, true, false) then return true end
end
end
end
@@ -545,7 +609,7 @@ function br.RaidBuff(BuffSlot, myBuffSpellID)
end
end
-function br.getUnitCluster(minUnits, maxRange, radius)
+function custom:getUnitCluster(minUnits, maxRange, radius)
-- Description:
-- returns the enemy with minUnits around in maxRange
@@ -563,11 +627,11 @@ function br.getUnitCluster(minUnits, maxRange, radius)
local enemiesInRange = 0
local theReturnUnit
- for k, _ in pairs(br.enemy) do
- local thisUnit = br.enemy[k].unit
- local thisEnemies = br.getNumEnemies(thisUnit, radius)
- if br.getLineOfSight(thisUnit) == true then
- if br.getDistance(thisUnit) < maxRange then
+ for k, _ in pairs(br.engines.enemiesEngine.enemy) do
+ local thisUnit = br.engines.enemiesEngine.enemy[k].unit
+ local thisEnemies = br.functions.combat:getNumEnemies(thisUnit, radius)
+ if br.functions.misc:getLineOfSight(thisUnit) == true then
+ if br.functions.range:getDistance(thisUnit) < maxRange then
if thisEnemies >= minUnits and thisEnemies > enemiesInRange then
theReturnUnit = thisUnit
end
@@ -577,7 +641,7 @@ function br.getUnitCluster(minUnits, maxRange, radius)
return select(1, theReturnUnit)
end
-function br.getBiggestUnitCluster(maxRange, radius, minCount)
+function custom:getBiggestUnitCluster(maxRange, radius, minCount)
-- Description:
-- returns the enemy with most enemies in radius in maxRange from player
@@ -596,12 +660,12 @@ function br.getBiggestUnitCluster(maxRange, radius, minCount)
local theReturnUnit
-- local foundCluster = false
- for k, _ in pairs(br.enemy) do
- local thisUnit = br.enemy[k].unit
- local thisRange = br.getDistance(thisUnit) or 99
- if br.getLineOfSight(thisUnit) == true then
+ for k, _ in pairs(br.engines.enemiesEngine.enemy) do
+ local thisUnit = br.engines.enemiesEngine.enemy[k].unit
+ local thisRange = br.functions.range:getDistance(thisUnit) or 99
+ if br.functions.misc:getLineOfSight(thisUnit) == true then
if thisRange < maxRange then
- local enemyCount = br.getNumEnemies(thisUnit, radius)
+ local enemyCount = br.functions.combat:getNumEnemies(thisUnit, radius)
if enemyCount >= enemiesInRange then
theReturnUnit = thisUnit
-- foundCluster = true
@@ -618,12 +682,12 @@ end
--[[ ]]
-- Used to merge two tables
-function br.mergeTables(a, b)
+function custom:mergeTables(a, b)
if a == nil then a = {} end
if type(a) == 'table' and type(b) == 'table' then
for k, v in pairs(b) do
if type(v) == 'table' and type(a[k] or false) == 'table' then
- br.mergeTables(a[k], v)
+ br.functions.custom:mergeTables(a[k], v)
else
a[k] = v
end
@@ -633,31 +697,31 @@ function br.mergeTables(a, b)
end
-- Used by new Class Framework to put all seperat Spell-Tables into new spell table
-function br.mergeSpellTables(tSpell, tCharacter, tClass, tSpec)
- tSpell = br.mergeTables(tSpell, tCharacter)
- tSpell = br.mergeTables(tSpell, tClass)
- tSpell = br.mergeTables(tSpell, tSpec)
+function custom:mergeSpellTables(tSpell, tCharacter, tClass, tSpec)
+ tSpell = br.functions.custom:mergeTables(tSpell, tCharacter)
+ tSpell = br.functions.custom:mergeTables(tSpell, tClass)
+ tSpell = br.functions.custom:mergeTables(tSpell, tSpec)
return tSpell
end
-function br.mergeIdTables(idTable)
+function custom:mergeIdTables(idTable)
local class = select(2, br._G.UnitClass("player"))
- local spec = br._G.GetSpecializationInfo(br._G.GetSpecialization())
+ local spec = br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization())
if idTable ~= nil then idTable = {} end
if br.lists.spells.Shared ~= nil then
- idTable = br.mergeTables(idTable, br.lists.spells.Shared)
+ idTable = br.functions.custom:mergeTables(idTable, br.lists.spells.Shared)
end
if br.lists.spells[class] ~= nil then
if br.lists.spells[class].Shared ~= nil then
- idTable = br.mergeTables(idTable, br.lists.spells[class].Shared)
+ idTable = br.functions.custom:mergeTables(idTable, br.lists.spells[class].Shared)
if br.lists.spells[class].Shared.abilities ~= nil then
- idTable = br.mergeTables(idTable, br.lists.spells[class].Shared.abilities)
+ idTable = br.functions.custom:mergeTables(idTable, br.lists.spells[class].Shared.abilities)
end
end
if br.lists.spells[class][spec] ~= nil then
- idTable = br.mergeTables(idTable, br.lists.spells[class][spec])
+ idTable = br.functions.custom:mergeTables(idTable, br.lists.spells[class][spec])
if br.lists.spells[class][spec].abilities ~= nil then
- idTable = br.mergeTables(idTable, br.lists.spells[class][spec].abilities)
+ idTable = br.functions.custom:mergeTables(idTable, br.lists.spells[class][spec].abilities)
end
end
end
@@ -665,10 +729,10 @@ function br.mergeIdTables(idTable)
end
-- takes a given time duration and returns a string representing the hours, minutes, and seconds
--- examply br.formattedTime(time,format))
+-- examply br.functions.custom:formattedTime(time,format))
-- format 1 = short time: HH:MM:SS
-- format 2 = long time: 2hrs 24m 15s
-function br.formattedTime(inTime, format)
+function custom:formattedTime(inTime, format)
if format == nil then format = 1 end
inTime = inTime / 1000
local hours = math.floor(inTime / 3600)
@@ -683,7 +747,7 @@ end
-- inTable(myTable, "hello") == true
-- inTable(myTable, "WHAT?") == false
-- check if tContains() does the same wow api
-function br.inTable(tbl, item)
+function custom:inTable(tbl, item)
for key, value in pairs(tbl) do
if value == item then return key end
end
@@ -692,7 +756,7 @@ end
--- Inserts table values into a table
-- No nested table (table in a table)
-function br.insertTableIntoTable(originalTable, insertTable)
+function custom:insertTableIntoTable(originalTable, insertTable)
for i = 1, #insertTable do
table.insert(originalTable, insertTable[i])
end
@@ -700,7 +764,7 @@ end
--- Returns if specified trinket is equipped in either slot
-- if isTrinketEquipped(124518) then trinket = "Libram of Vindication" end
-function br.isTrinketEquipped(trinket)
+function custom:isTrinketEquipped(trinket)
if (br._G.GetInventoryItemID("player", 13) == trinket or br._G.GetInventoryItemID("player", 14) == trinket) then
return true
else
@@ -711,13 +775,13 @@ end
--- Return true if player has buff X
-- Parameter: ID
-- hasBuff(12345)
-function br.hasBuff(spellID)
+function custom:hasBuff(spellID)
local buffs, i = {}, 1
- local buff = br.UnitBuff("player", i)
+ local buff = br.functions.aura:UnitBuff("player", i)
while buff do
buffs[#buffs + 1] = buff
i = i + 1
- buff = select(10, br.UnitBuff("player", i))
+ buff = select(10, br.functions.aura:UnitBuff("player", i))
if buff ~= nil then
if buff == spellID then return true end
end
@@ -728,13 +792,13 @@ end
--- Cancel the giving BuffID
-- Parameter: ID
-- cancelBuff(12345)
-function br.cancelBuff(spellID)
+function custom:cancelBuff(spellID)
local buffs, i = {}, 1
- local buff = br.UnitBuff("player", i)
+ local buff = br.functions.aura:UnitBuff("player", i)
while buff do
buffs[#buffs + 1] = buff
i = i + 1
- buff = select(10, br.UnitBuff("player", i))
+ buff = select(10, br.functions.aura:UnitBuff("player", i))
if buff ~= nil then
if buff == spellID then
br._G.CancelUnitBuff("player", i, "")
@@ -747,46 +811,46 @@ end
--[[ DBM Timer ]] --
-br.DBM = {}
+br.functions.DBM = {}
--- Return: All current DBM Timer
-function br.DBM:getBars()
+function br.functions.DBM:getBars()
if br._G.DBM then
- if not br.DBM.Timer then
- br.DBM.Timer = {}
+ if not br.functions.DBM.Timer then
+ br.functions.DBM.Timer = {}
else
- br._G.wipe(br.DBM.Timer)
+ br._G.wipe(br.functions.DBM.Timer)
end
if br._G.DBM.Bars ~= nil then
for bar in pairs(br._G.DBM.Bars.bars) do
local number = string.match(bar.id, "%d+")
- table.insert(br.DBM.Timer, { id = bar.id, timer = bar.timer, spellid = number })
+ table.insert(br.functions.DBM.Timer, { id = bar.id, timer = bar.timer, spellid = number })
end
end
end
end
--- Usage:
--- 1 - br.DBM:getPulltimer() -> return (number) pulltimer count
--- 2 - br.DBM:getPulltimer(5) -> return (boolean) if pulltimer is below given time TRUE else FALSE
+-- 1 - br.functions.DBM:getPulltimer() -> return (number) pulltimer count
+-- 2 - br.functions.DBM:getPulltimer(5) -> return (boolean) if pulltimer is below given time TRUE else FALSE
-- specificID can be set if Pulltimer is NOT "Pull in"
-function br.DBM:getPulltimer(time, specificID)
- if br.DBM.Timer then
+function br.functions.DBM:getPulltimer(time, specificID)
+ if br.functions.DBM.Timer then
if br._G.C_AddOns.IsAddOnLoaded('DBM-Core') then
local specificID = specificID or "Pull in"
local hasPullTimer = false
local isBelowTime = false
local pullTimer = 0
- for i = 1, #br.DBM.Timer do
+ for i = 1, #br.functions.DBM.Timer do
-- Check if a Pulltimer is present
- --Print("get pull timer id="..br.DBM.Timer[i].id)
- --Print("time="..br.DBM.Timer[i].timer)
+ --Print("get pull timer id="..br.functions.DBM.Timer[i].id)
+ --Print("time="..br.functions.DBM.Timer[i].timer)
- --if br.DBM.Timer[i].id == specificID then
- local is_find, _ = string.find(br.DBM.Timer[i].id, tostring(specificID))
+ --if br.functions.DBM.Timer[i].id == specificID then
+ local is_find, _ = string.find(br.functions.DBM.Timer[i].id, tostring(specificID))
if is_find ~= nil then
hasPullTimer = true
- pullTimer = br.DBM.Timer[i].timer
+ pullTimer = br.functions.DBM.Timer[i].timer
-- if a time is given set var to true
if time then
if pullTimer <= time then
@@ -809,11 +873,11 @@ function br.DBM:getPulltimer(time, specificID)
local isBelowTime = false
local currentTimer = 0
local specificID = specificID or "Pull"
- for i = 1, #br.DBM.Timer do
+ for i = 1, #br.functions.DBM.Timer do
-- Check if timer with spell id is present
- if br.DBM.Timer[i] ~= nil and br.DBM.Timer[i].id == specificID then
+ if br.functions.DBM.Timer[i] ~= nil and br.functions.DBM.Timer[i].id == specificID then
hasTimer = true
- currentTimer = br.DBM.Timer[i].exptime - br._G.GetTime()
+ currentTimer = br.functions.DBM.Timer[i].exptime - br._G.GetTime()
-- if a time is given set var to true
if time then
if currentTimer <= time then
@@ -839,22 +903,22 @@ function br.DBM:getPulltimer(time, specificID)
return 999 -- return number to avoid conflicts but to high so it should never trigger
end
--- function br.DBM:getPulltimer(time, specificID)
--- if br.DBM.Timer then
+-- function br.functions.DBM:getPulltimer(time, specificID)
+-- if br.functions.DBM.Timer then
-- specificID = specificID or "Pull in"
-- local hasPulltimer = false
-- local isBelowTime = false
-- local pullTimer = 0
--- for i = 1, #br.DBM.Timer do
+-- for i = 1, #br.functions.DBM.Timer do
-- -- Check if a Pulltimer is present
--- --Print("get pull timer id="..br.DBM.Timer[i].id)
--- --Print("time="..br.DBM.Timer[i].timer)
+-- --Print("get pull timer id="..br.functions.DBM.Timer[i].id)
+-- --Print("time="..br.functions.DBM.Timer[i].timer)
--- --if br.DBM.Timer[i].id == specificID then
--- is_find , _ = string.find(br.DBM.Timer[i].id , tostring(specificID))
+-- --if br.functions.DBM.Timer[i].id == specificID then
+-- is_find , _ = string.find(br.functions.DBM.Timer[i].id , tostring(specificID))
-- if is_find ~= nil then
-- hasPulltimer = true
--- pullTimer = br.DBM.Timer[i].timer
+-- pullTimer = br.functions.DBM.Timer[i].timer
-- -- if a time is given set var to true
-- if time then
@@ -884,19 +948,19 @@ end
--- Usage:
--- 1 - br.DBM:getTimer(spellID) -> return (number) the count of given spell ID timer
--- 2 - br.DBM:getTimer(spellID, time) -> return (boolean) TRUE if spellid is below given time else FALSE
-function br.DBM:getTimer(spellID, time)
- if br.DBM.Timer then
+-- 1 - br.functions.DBM:getTimer(spellID) -> return (number) the count of given spell ID timer
+-- 2 - br.functions.DBM:getTimer(spellID, time) -> return (boolean) TRUE if spellid is below given time else FALSE
+function br.functions.DBM:getTimer(spellID, time)
+ if br.functions.DBM.Timer then
if br._G.C_AddOns.IsAddOnLoaded('DBM-Core') then
local hasTimer = false
local isBelowTime = false
local currentTimer = 0
- for i = 1, #br.DBM.Timer do
+ for i = 1, #br.functions.DBM.Timer do
-- Check if timer with spell id is present
- if tonumber(br.DBM.Timer[i].spellid) == spellID then
+ if tonumber(br.functions.DBM.Timer[i].spellid) == spellID then
hasTimer = true
- currentTimer = br.DBM.Timer[i].timer
+ currentTimer = br.functions.DBM.Timer[i].timer
-- if a time is given set var to true
if time then
if currentTimer <= time then
@@ -918,11 +982,11 @@ function br.DBM:getTimer(spellID, time)
local hasTimer = false
local isBelowTime = false
local currentTimer = 0
- for i = 1, #br.DBM.Timer do
+ for i = 1, #br.functions.DBM.Timer do
-- Check if timer with spell id is present
- if br.DBM.Timer[i] ~= nil and br.DBM.Timer[i].id == spellID then
+ if br.functions.DBM.Timer[i] ~= nil and br.functions.DBM.Timer[i].id == spellID then
hasTimer = true
- currentTimer = br.DBM.Timer[i].exptime - br._G.GetTime()
+ currentTimer = br.functions.DBM.Timer[i].exptime - br._G.GetTime()
-- if a time is given set var to true
if time then
if currentTimer <= time then
@@ -949,27 +1013,27 @@ function br.DBM:getTimer(spellID, time)
end
-- Future position
-function br.GetFuturePostion(unit, castTime)
+function custom:GetFuturePostion(unit, castTime)
local distance = br._G.GetUnitSpeed(unit) * castTime
if distance > 0 then
- local x, y, z = br.GetObjectPosition(unit)
- local angle = br.GetObjectFacing(unit)
+ local x, y, z = br.functions.unit:GetObjectPosition(unit)
+ local angle = br.functions.unit:GetObjectFacing(unit)
--If Unit have a target, let's make sure they don't collide
local unitTarget = br._G.UnitTarget(unit)
local unitTargetDist = 0
if unitTarget ~= nil then
- local tX, tY, tZ = br.GetObjectPosition(unitTarget)
+ local tX, tY, tZ = br.functions.unit:GetObjectPosition(unitTarget)
--Lets get predicted position of unit target aswell
if br._G.GetUnitSpeed(unitTarget) > 0 then
local tDistance = br._G.GetUnitSpeed(unitTarget) * castTime
- local tAngle = br.GetObjectFacing(unitTarget)
+ local tAngle = br.functions.unit:GetObjectFacing(unitTarget)
tX = tX + cos(tAngle) * tDistance
tY = tY + sin(tAngle) * tDistance
unitTargetDist = sqrt(((tX - x) ^ 2) + ((tY - y) ^ 2) + ((tZ - z) ^ 2)) -
((br._G.UnitCombatReach(unit) or 0) + (br._G.UnitCombatReach(unitTarget) or 0))
if unitTargetDist < distance then distance = unitTargetDist end
else
- unitTargetDist = br.getDistance(unitTarget, unit, "dist")
+ unitTargetDist = br.functions.range:getDistance(unitTarget, unit, "dist")
if unitTargetDist < distance then distance = unitTargetDist end
end
-- calculate angle based on target position/future position
@@ -982,12 +1046,12 @@ function br.GetFuturePostion(unit, castTime)
y = y + sin(angle) * distance
return x, y, z
end
- return br.GetObjectPosition(unit)
+ return br.functions.unit:GetObjectPosition(unit)
end
-function br.PullTimerRemain(returnBool)
+function custom:PullTimerRemain(returnBool)
if returnBool == nil then returnBool = false end
- if br.DBM:getPulltimer() == 999 then
+ if br.functions.DBM:getPulltimer() == 999 then
if returnBool == false then
return 999
else
@@ -995,20 +1059,20 @@ function br.PullTimerRemain(returnBool)
end
else
if returnBool == false then
- return br.DBM:getPulltimer()
+ return br.functions.DBM:getPulltimer()
else
return true
end
end
end
-function br.BWInit()
- if not br.DBM.Timer then
- br.DBM.Timer = {}
+function custom:BWInit()
+ if not br.functions.DBM.Timer then
+ br.functions.DBM.Timer = {}
end
- if br.DBM.BigWigs ~= nil then return end
- br.DBM.BigWigs = {}
- local BigWigs = br.DBM.BigWigs
+ if br.functions.DBM.BigWigs ~= nil then return end
+ br.functions.DBM.BigWigs = {}
+ local BigWigs = br.functions.DBM.BigWigs
BigWigs.callback = {}
local callback = BigWigs.callback
BigWigs.BigwigsCallback = function(event, ...)
@@ -1026,10 +1090,10 @@ function br.BWInit()
return
end
end
- for i = 1, #br.DBM.Timer do
- if br.DBM.Timer[i] ~= nil and br.DBM.Timer[i].id == spellId then
+ for i = 1, #br.functions.DBM.Timer do
+ if br.functions.DBM.Timer[i] ~= nil and br.functions.DBM.Timer[i].id == spellId then
clone = true
- br.DBM.Timer[i].exptime = br._G.GetTime() + duration
+ br.functions.DBM.Timer[i].exptime = br._G.GetTime() + duration
break
end
end
@@ -1037,16 +1101,16 @@ function br.BWInit()
local timer = {}
timer.id = spellId
timer.exptime = br._G.GetTime() + duration
- br._G.tinsert(br.DBM.Timer, timer)
+ br._G.tinsert(br.functions.DBM.Timer, timer)
clone = false
end
elseif (event == "BigWigs_StopBars"
or event == "BigWigs_OnBossDisable"
or event == "BigWigs_OnPluginDisable") then
- if #br.DBM.Timer > 0 then
- local count = #br.DBM.Timer
+ if #br.functions.DBM.Timer > 0 then
+ local count = #br.functions.DBM.Timer
for i = 0, count do
- br.DBM.Timer[i] = nil
+ br.functions.DBM.Timer[i] = nil
end
end
else
@@ -1062,12 +1126,12 @@ function br.BWInit()
end
end
-function br.BWCheck()
- if #br.DBM.Timer > 0 then
- for i = 1, #br.DBM.Timer do
- if br.DBM.Timer[i] ~= nil then
- if br.DBM.Timer[i].exptime < br._G.GetTime() then
- br.DBM.Timer[i] = nil
+function custom:BWCheck()
+ if #br.functions.DBM.Timer > 0 then
+ for i = 1, #br.functions.DBM.Timer do
+ if br.functions.DBM.Timer[i] ~= nil then
+ if br.functions.DBM.Timer[i].exptime < br._G.GetTime() then
+ br.functions.DBM.Timer[i] = nil
end
else
end
@@ -1076,6 +1140,6 @@ function br.BWCheck()
end
-- Check Instance IDs from https://wow.gamepedia.com/InstanceID
-function br.getCurrentZoneId()
+function custom:getCurrentZoneId()
return select(8, br._G.GetInstanceInfo())
end
diff --git a/System/Functions/Glyph.lua b/System/Functions/Glyph.lua
new file mode 100644
index 0000000000..455d2eddf9
--- /dev/null
+++ b/System/Functions/Glyph.lua
@@ -0,0 +1,37 @@
+local _, br = ...
+
+br.functions.glyph = br.functions.glyph or {}
+local glyph = br.functions.glyph
+
+-- Defensive wrapper: retail clients may not support classic glyph APIs.
+function glyph:isAvailable()
+ return type(br._G.GetGlyphSocketInfo) == "function"
+end
+
+function glyph:getSocketCount()
+ if type(br._G.GetNumGlyphSockets) == "function" then
+ local n = br._G.GetNumGlyphSockets()
+ if type(n) == "number" and n > 0 then
+ return n
+ end
+ end
+ -- Legacy: historically 6 glyph sockets.
+ return 6
+end
+
+-- Returns true if a glyph (by glyphSpellId) is currently socketed.
+function glyph:hasGlyph(glyphSpellId)
+ if type(glyphSpellId) ~= "number" or glyphSpellId <= 0 then return false end
+ if not self:isAvailable() then return false end
+
+ local socketCount = self:getSocketCount()
+ for i = 1, socketCount do
+ -- Historically, socket info includes the current glyph spell ID in either the 4th or 6th return value.
+ local _, _, _, glyphId1, _, glyphId2 = br._G.GetGlyphSocketInfo(i)
+ if glyphId1 == glyphSpellId or glyphId2 == glyphSpellId then
+ return true
+ end
+ end
+
+ return false
+end
diff --git a/System/Functions/InFlight.lua b/System/Functions/InFlight.lua
index c36f23a58f..75c0d3750f 100644
--- a/System/Functions/InFlight.lua
+++ b/System/Functions/InFlight.lua
@@ -1,6 +1,6 @@
local _, br = ...
-br.InFlight = {}
-local InFlight = br.InFlight
+br.functions.InFlight = br.functions.InFlight or {}
+local InFlight = br.functions.InFlight
InFlight.Tracker = {}
local projectileSpeed = {
@@ -11,7 +11,7 @@ local projectileSpeed = {
[214634] = 30, --Ebonbolt
}
-function InFlight.Hit(spellID, destinationGUID, sourceGUID)
+function InFlight:Hit(spellID, destinationGUID, sourceGUID)
local target, source
local function distance(unit1, unit2)
local x1, y1, z1 = br._G.ObjectPosition(unit1)
@@ -36,21 +36,21 @@ function InFlight.Hit(spellID, destinationGUID, sourceGUID)
return (distance(source, target) / projectileSpeed[spellID])
end
-function InFlight.Check(spellID, destination)
- if InFlight.Tracker[spellID] and InFlight.Tracker[spellID].HitTime > br._G.GetTime() and (not destination or not InFlight.Tracker[spellID].Target or br.GetUnitIsUnit(InFlight.Tracker[spellID].Target, destination)) then
+function InFlight:Check(spellID, destination)
+ if InFlight.Tracker[spellID] and InFlight.Tracker[spellID].HitTime > br._G.GetTime() and (not destination or not InFlight.Tracker[spellID].Target or br.functions.unit:GetUnitIsUnit(InFlight.Tracker[spellID].Target, destination)) then
return true
end
return false
end
-function InFlight.Remain(spellID, destination)
+function InFlight:Remain(spellID, destination)
if InFlight.Check(spellID, destination) then
return InFlight.Tracker[spellID].HitTime - br._G.GetTime()
end
return 0
end
-function InFlight.Add(spellID, destinationGUID, sourceGUID)
+function InFlight:Add(spellID, destinationGUID, sourceGUID)
if InFlight.Tracker[spellID] == nil then
InFlight.Tracker[spellID] = {}
end
diff --git a/System/Functions/Item.lua b/System/Functions/Item.lua
index 9d86ef6d9d..9d6677a3db 100644
--- a/System/Functions/Item.lua
+++ b/System/Functions/Item.lua
@@ -1,6 +1,9 @@
local _, br = ...
+br.functions.item = br.functions.item or {}
+local item = br.functions.item
+
-- item charges
-function br.itemCharges(itemID)
+function item:itemCharges(itemID)
local charges = br._G.GetItemCount(itemID, false, true)
if charges == nil then
return 0
@@ -8,9 +11,9 @@ function br.itemCharges(itemID)
return charges
end
--- if br.canUseItem(1710) then
-function br.canUseItem(itemID)
- if itemID == 0 or br.getHP("player") == 0 then
+-- if br.functions.item:canUseItem(1710) then
+function item:canUseItem(itemID)
+ if itemID == 0 or br.functions.unit:getHP("player") == 0 then
return false
end
if itemID <= 19 then
@@ -19,14 +22,14 @@ function br.canUseItem(itemID)
return false
end
if br._G.GetItemSpell(slotItemID) ~= nil then
- if br._G.GetItemCooldown(slotItemID) == 0 and br._G.IsUsableItem(slotItemID) then
+ if br._G.C_Container.GetItemCooldown(slotItemID) == 0 and br._G.IsUsableItem(slotItemID) then
return true
end
end
elseif (br._G.GetItemCount(itemID, false, false) > 0 or br._G.PlayerHasToy(itemID)) and
- ((br._G.IsEquippableItem(itemID) and br._G.IsEquippedItem(itemID)) or (not br._G.IsEquippableItem(itemID) and br.hasItem(itemID)))
+ ((br._G.IsEquippableItem(itemID) and br._G.IsEquippedItem(itemID)) or (not br._G.IsEquippableItem(itemID) and item:hasItem(itemID)))
then
- if itemID > 19 and br._G.GetItemCooldown(itemID) == 0 and (br._G.IsUsableItem(itemID) or br._G.C_Spell.IsSpellUsable(select(2, br._G.GetItemSpell(itemID)))) then
+ if itemID > 19 and br._G.C_Container.GetItemCooldown(itemID) == 0 and (br._G.IsUsableItem(itemID) or br._G.C_Spell.IsSpellUsable(select(2, br._G.GetItemSpell(itemID)))) then
return true
end
end
@@ -34,7 +37,7 @@ function br.canUseItem(itemID)
end
-- if canTrinket(13) then
-function br.canTrinket(trinketSlot)
+function item:canTrinket(trinketSlot)
if trinketSlot == 13 or trinketSlot == 14 then
if trinketSlot == 13 and select(3, br._G.GetInventoryItemCooldown("player", 13)) == 1 and br._G.GetInventoryItemCooldown("player", 13) <= br.player.gcdMax then
return true
@@ -47,8 +50,8 @@ function br.canTrinket(trinketSlot)
end
end
--- if br.hasItem(1234) == true then
-function br.hasItem(itemID)
+-- if br.functions.item:hasItem(1234) == true then
+function item:hasItem(itemID)
if br._G.PlayerHasToy(itemID) then
return true
end
@@ -67,13 +70,13 @@ function br.hasItem(itemID)
return itemFound
end
--- br.useItem(12345)
-function br.useItem(itemID, thisUnit)
+-- br.functions.item:useItem(12345)
+function item:useItem(itemID, thisUnit)
br.itemSpamDelay = br.itemSpamDelay or 0
if itemID <= 19 then
if br._G.GetItemSpell(br._G.GetInventoryItemID("player", itemID)) ~= nil then
local slotItemID = br._G.GetInventoryItemID("player", itemID)
- if br._G.GetItemCooldown(slotItemID) == 0 then
+ if br._G.C_Container.GetItemCooldown(slotItemID) == 0 then
if not br.itemSpamDelay or br._G.GetTime() > br.itemSpamDelay then
-- br._G.RunMacroText("/use " .. select(1, GetItemInfo(slotItemID)))
br._G.UseItemByName(select(1, br._G.GetItemInfo(slotItemID)), thisUnit);
@@ -83,7 +86,7 @@ function br.useItem(itemID, thisUnit)
end
end
elseif itemID > 19 and (br._G.GetItemCount(itemID) > 0 or br._G.PlayerHasToy(itemID)) then
- if br._G.GetItemCooldown(itemID) == 0 then
+ if br._G.C_Container.GetItemCooldown(itemID) == 0 then
if not br.itemSpamDelay or br._G.GetTime() > br.itemSpamDelay then
-- br._G.RunMacroText("/use " .. select(1, GetItemInfo(itemID)))
br._G.UseItemByName(select(1, br._G.GetItemInfo(itemID)), thisUnit);
@@ -95,19 +98,35 @@ function br.useItem(itemID, thisUnit)
return false
end
-function br.useItemGround(Unit, itemID, maxDistance, minDistance, radius)
+-- Get the best (highest rank) item the player has from a ranked table.
+-- Mirrors br.functions.spell:getHighestKnownRank for items.
+-- @param itemIDTable - A table of item IDs {rank1, rank2, ..., rankN} ordered lowest to highest rank.
+-- @return itemID - The highest rank item ID the player currently has in their bags, or nil if none.
+function item:getHighestHeldRank(itemIDTable)
+ if type(itemIDTable) ~= "table" then
+ return itemIDTable
+ end
+ for i = #itemIDTable, 1, -1 do
+ if self:hasItem(itemIDTable[i]) then
+ return itemIDTable[i]
+ end
+ end
+ return nil
+end
+
+function item:useItemGround(Unit, itemID, maxDistance, minDistance, radius)
if radius == nil then
radius = maxDistance
end
if minDistance == nil then
minDistance = 0
end
- if br.GetUnitExists(Unit) and br.getLineOfSight("player", Unit) and br.getDistance("player", Unit) < maxDistance and
- br.getDistance("player", Unit) >= minDistance and
- #br.getEnemies(Unit, radius) >= #br.getEnemies(Unit, radius, true)
+ if br.functions.unit:GetUnitExists(Unit) and br.functions.misc:getLineOfSight("player", Unit) and br.functions.range:getDistance("player", Unit) < maxDistance and
+ br.functions.range:getDistance("player", Unit) >= minDistance and
+ #br.engines.enemiesEngineFunctions:getEnemies(Unit, radius) >= #br.engines.enemiesEngineFunctions:getEnemies(Unit, radius, true)
then
- br.useItem(itemID)
- local X, Y, Z = br.GetObjectPosition(Unit)
+ br.functions.item:useItem(itemID)
+ local X, Y, Z = br.functions.unit:GetObjectPosition(Unit)
--local distanceToGround = getGroundDistance(Unit) or 0
br._G.ClickPosition(X, Y, Z) --distanceToGround
return true
@@ -115,10 +134,11 @@ function br.useItemGround(Unit, itemID, maxDistance, minDistance, radius)
return false
end
-function br.hasHealthPot()
+function item:hasHealthPot()
local locale = br._G.GetLocale()
if locale ~= "enUS" and locale ~= "enGB" then
- if br.hasItem(169451) then
+ local abyssalHealingPotion = br.functions.item:getHighestItemID("abyssalHealingPotion") or 169451
+ if br.functions.item:hasItem(abyssalHealingPotion) then
return true
end
end
@@ -133,11 +153,12 @@ function br.hasHealthPot()
end
end
-function br.getHealthPot()
+function item:getHealthPot()
local locale = br._G.GetLocale()
if locale ~= "enUS" and locale ~= "enGB" then
- if br.hasItem(171267) then
- return 171267
+ local coastalHealingPotion = br.functions.item:getHighestItemID("coastalHealingPotion") or 171267
+ if br.functions.item:hasItem(coastalHealingPotion) then
+ return coastalHealingPotion
end
end
local potion = br.player.potion
@@ -159,7 +180,7 @@ function br.getHealthPot()
end
-- if TierScan("T17")>=2 then
-function br.TierScan(thisTier)
+function item:TierScan(thisTier)
local equippedItems = 0
local myClass = select(2, br._G.UnitClass("player"))
thisTier = string.upper(thisTier)
@@ -180,7 +201,7 @@ function br.TierScan(thisTier)
return equippedItems
end
-function br.hasEquiped(ItemID, Slot)
+function item:hasEquiped(ItemID, Slot)
if ItemID == nil then return false end
if br._G.PlayerHasToy(ItemID) then
return true
@@ -201,3 +222,36 @@ function br.hasEquiped(ItemID, Slot)
end
return foundItem
end
+
+function item:getHeirloomNeck()
+ if not br.lists or not br.lists.items then return 0 end
+ local items = br.lists.items
+ local neckKeys = {
+ "eternalAmuletOfTheRedeemed",
+ "eternalEmberfuryTalisman",
+ "eternalHorizonChoker",
+ "eternalTalismanOfEvasion",
+ "eternalWillOfTheMartyr",
+ "eternalWovenIvyNecklace",
+ "manariTrainingAmulet",
+ }
+ for _, k in ipairs(neckKeys) do
+ local id = items[k]
+ if id and br.functions.item:hasEquiped(id, 2) then return id end
+ end
+ return 0
+end
+
+-- Look up an item ID from br.lists.items by key name.
+-- If the found value is a ranked table, returns the highest rank the player currently holds.
+-- @param key - The item name key as it appears in the item list (e.g. "abyssalHealingPotion")
+-- @return number|nil - The item ID, or nil if the list is not loaded or the key is not found.
+function item:getHighestItemID(key)
+ if not br.lists or not br.lists.items then return nil end
+ local v = br.lists.items[key]
+ if v == nil then return nil end
+ if type(v) == "table" then
+ return br.functions.item:getHighestHeldRank(v)
+ end
+ return v
+end
\ No newline at end of file
diff --git a/System/Functions/LastCast.lua b/System/Functions/LastCast.lua
index 9e1a148206..89ccd26b78 100644
--- a/System/Functions/LastCast.lua
+++ b/System/Functions/LastCast.lua
@@ -1,9 +1,14 @@
local _, br = ...
-br.lastCastTable = {}
-br.lastCastTable.tracker = {}
-br.lastCastTable.castTime = {}
-local tracker = br.lastCastTable.tracker
-local castTime = br.lastCastTable.castTime
+br.functions.lastCast = br.functions.lastCast or {}
+local lastCast = br.functions.lastCast
+
+lastCast.lastCastTable = {}
+lastCast.lastCastTable.tracker = {}
+lastCast.lastCastTable.castTime = {}
+lastCast.lastCastTable.failedTime = {}
+local tracker = lastCast.lastCastTable.tracker
+local castTime = lastCast.lastCastTable.castTime
+local failedTime = lastCast.lastCastTable.failedTime
local waitForSuccess
local lastCastFrame = br._G.CreateFrame("Frame")
@@ -20,7 +25,7 @@ local function addSpell(spellID)
if #tracker == 10 then
tracker[10] = nil
end
- br.lastCast = spellID -- legacy support for some rotations reading this in locals
+ lastCast.lastCast= spellID -- legacy support for some rotations reading this in locals
end
end
end
@@ -36,29 +41,35 @@ end
local function eventTracker(_, event, ...)
local sourceUnit = select(1, ...)
local spellID = event == "UNIT_SPELLCAST_SENT" and select(4, ...) or select(3, ...)
- local spellName = br._G.GetSpellInfo(spellID)
+ local spellName = br.api.wow.GetSpellInfo(spellID)
if sourceUnit == "player" and br.player and not ignoreList[spellID] then
if event == "UNIT_SPELLCAST_SENT" or event == "UNIT_SPELLCAST_START"
- or event == "UNIT_SPELLCAST_CHANNEL_START" or event == "UNIT_SPELLCAST_EMPOWER_START"
+ or event == "UNIT_SPELLCAST_CHANNEL_START" --or event == "UNIT_SPELLCAST_EMPOWER_START"
then
-- if event == "UNIT_SPELLCAST_SENT" then br._G.print("Sent Cast Spell: " .. spellName) end
-- if event == "UNIT_SPELLCAST_START" then br._G.print("Start Cast Spell: " .. spellName) end
-- if event == "UNIT_SPELLCAST_CHANNEL_START" then br._G.print("Start Channel Spell: " .. spellName) end
-- if event == "UNIT_SPELLCAST_EMPOWER_START" then br._G.print("Sent Empower Spell: " .. spellName) end
waitForSuccess = spellID
- addSpell(spellID)
- addCastTime(spellID)
+ -- Only add to tracker on SENT event to avoid duplicates
+ if event == "UNIT_SPELLCAST_SENT" then
+ addSpell(spellID)
+ end
+ -- addCastTime(spellID) -- Don't add cast time on SENT event, as the cast may not actually start (could be interrupted/failed before START event)
end
if event == "UNIT_SPELLCAST_SUCCEEDED" then
- -- if event == "UNIT_SPELLCAST_SUCCEEDED" then br._G.print("Success Cast Spell: " .. spellName) end
+ -- Record the actual completion time for the spell (useful for timeSinceLast)
+ addCastTime(spellID)
if waitForSuccess == spellID then
waitForSuccess = nil
else
+ -- Instant cast spell (no SENT/START event, only SUCCESS)
+ -- OR spell that completed after being interrupted/failed
addSpell(spellID)
addCastTime(spellID)
end
end
- if event == "UNIT_SPELLCAST_STOP" or event == "UNIT_SPELLCAST_CHANNEL_STOP" or event == "UNIT_SPELLCAST_EMPOWER_STOP"
+ if event == "UNIT_SPELLCAST_STOP" or event == "UNIT_SPELLCAST_CHANNEL_STOP" --or event == "UNIT_SPELLCAST_EMPOWER_STOP"
or event == "UNIT_SPELLCAST_FAILED" or event == "UNIT_SPELLCAST_FAILED_QUIET" or event == "UNIT_SPELLCAST_INTERRUPTED"
then
-- if event == "UNIT_SPELLCAST_STOP" then br._G.print("Stop Cast Spell: " .. spellName) end
@@ -70,7 +81,13 @@ local function eventTracker(_, event, ...)
if waitForSuccess == spellID then
br._G.tremove(tracker, 1)
waitForSuccess = nil
- br.lastCast = tracker[1]
+ lastCast.lastCast= tracker[1]
+ end
+ if event == "UNIT_SPELLCAST_FAILED" or event == "UNIT_SPELLCAST_FAILED_QUIET" or event == "UNIT_SPELLCAST_INTERRUPTED" then
+ failedTime[spellID] = br._G.GetTime()
+ lastCast.lastCastTable.lastFailedSpellID = spellID
+ lastCast.lastCastTable.lastFailedAt = failedTime[spellID]
+ lastCast.lastCastTable.lastFailedEvent = event
end
end
end
@@ -80,11 +97,11 @@ end
lastCastFrame:RegisterEvent("UNIT_SPELLCAST_SENT") -- Fired when a unit attempts to cast a spell regardless of the success of the cast.
lastCastFrame:RegisterEvent("UNIT_SPELLCAST_START") -- Fired when a unit begins casting a non-instant cast spell.
lastCastFrame:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START") -- Fired when a unit begins channeling in the course of casting a spell.
-lastCastFrame:RegisterEvent("UNIT_SPELLCAST_EMPOWER_START")
+-- lastCastFrame:RegisterEvent("UNIT_SPELLCAST_EMPOWER_START")
-- Stop Cast Events
lastCastFrame:RegisterEvent("UNIT_SPELLCAST_STOP") -- Fired when a unit stops casting.
lastCastFrame:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP") -- Fired when a unit stops channeling.
-lastCastFrame:RegisterEvent("UNIT_SPELLCAST_EMPOWER_STOP")
+-- lastCastFrame:RegisterEvent("UNIT_SPELLCAST_EMPOWER_STOP")
lastCastFrame:RegisterEvent("UNIT_SPELLCAST_FAILED") -- Fired when a unit's spellcast fails.
lastCastFrame:RegisterEvent("UNIT_SPELLCAST_FAILED_QUIET")
lastCastFrame:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED") -- Fired when a unit's spellcast is interrupted
diff --git a/System/Functions/Misc.lua b/System/Functions/Misc.lua
index a4cd6bd56e..5a5c425ebb 100644
--- a/System/Functions/Misc.lua
+++ b/System/Functions/Misc.lua
@@ -1,6 +1,8 @@
local _, br = ...
+br.functions.misc = {}
+local misc = br.functions.misc
-- getLatency()
-function br.getLatency()
+function misc:getLatency()
-- local lag = ((select(3,GetNetStats()) + select(4,GetNetStats())) / 1000)
local lag = select(4, br._G.GetNetStats()) / 1000
if lag < .05 then
@@ -12,16 +14,16 @@ function br.getLatency()
end
--Calculate Agility
-function br.getAgility()
+function misc:getAgility()
local AgiBase, _, AgiPos, AgiNeg = br._G.UnitStat("player", 2)
local Agi = AgiBase + AgiPos + AgiNeg
return Agi
end
-function br.getFallDistance()
+function misc:getFallDistance()
local zDist
local zCoord = nil
- local _, _, position = br.GetObjectPosition("player")
+ local _, _, position = br.functions.unit:GetObjectPosition("player")
if position == nil then return 0 end
if zCoord == nil then
@@ -40,14 +42,14 @@ function br.getFallDistance()
end
--if getFallTime() > 2 then
-function br.getFallTime()
+function misc:getFallTime()
if br.fallStarted == nil then
br.fallStarted = 0
end
if br.fallTime == nil then
br.fallTime = 0
end
- if br._G.IsFalling() and br.getFallDistance() < 0 then
+ if br._G.IsFalling() and br.functions.misc:getFallDistance() < 0 then
if br.fallStarted == 0 then
br.fallStarted = br._G.GetTime()
end
@@ -62,8 +64,8 @@ function br.getFallTime()
return br.fallTime
end
--- if br.getLineOfSight("target"[,"target"]) then
-br.doLinesIntersect = function(a, b, c, d)
+-- if br.functions.misc:getLineOfSight("target"[,"target"]) then
+misc.doLinesIntersect = function(a, b, c, d)
-- parameter conversion
local L1 = { X1 = a.x, Y1 = a.y, X2 = b.x, Y2 = b.y }
local L2 = { X1 = c.x, Y1 = c.y, X2 = d.x, Y2 = d.y }
@@ -99,7 +101,7 @@ br.doLinesIntersect = function(a, b, c, d)
return false
end
-function br.carapaceMath(Unit1, Unit2)
+function misc:carapaceMath(Unit1, Unit2)
if Unit2 == nil then
Unit2 = Unit1
if Unit2 == "player" then
@@ -109,8 +111,8 @@ function br.carapaceMath(Unit1, Unit2)
end
end
if (br.player and br.player.eID and br.player.eID == 2337) then
- local pX, pY = br.GetObjectPosition(Unit1)
- local tX, tY = br.GetObjectPosition(Unit2)
+ local pX, pY = br.functions.unit:GetObjectPosition(Unit1)
+ local tX, tY = br.functions.unit:GetObjectPosition(Unit2)
-- local tentExists = false
local tentCheck
local tentX, tentY
@@ -124,15 +126,15 @@ function br.carapaceMath(Unit1, Unit2)
local objectid = br._G.ObjectID(object)
if objectid == 157485 then
-- tentExists = true
- local tentFacing = br.GetObjectFacing(object)
- tentX, tentY = br.GetObjectPosition(object)
+ local tentFacing = br.functions.unit:GetObjectFacing(object)
+ tentX, tentY = br.functions.unit:GetObjectPosition(object)
table.insert(
br.tentCache,
{
["tentFacing"] = tentFacing,
- ["tentX"] = select(1, br.GetObjectPosition(object)),
- ["tentY"] = select(2, br.GetObjectPosition(object)),
- ["tentZ"] = select(3, br.GetObjectPosition(object)),
+ ["tentX"] = select(1, br.functions.unit:GetObjectPosition(object)),
+ ["tentY"] = select(2, br.functions.unit:GetObjectPosition(object)),
+ ["tentZ"] = select(3, br.functions.unit:GetObjectPosition(object)),
["tentX2"] = tentX + (80 * math.cos(tentFacing)),
["tentY2"] = tentY + (80 * math.sin(tentFacing))
}
@@ -149,7 +151,7 @@ function br.carapaceMath(Unit1, Unit2)
local c = { x = tX, y = tY }
local d = { x = pX, y = pY }
if tX ~= nil then
- if br.doLinesIntersect(a, b, c, d) then
+ if misc.doLinesIntersect(a, b, c, d) then
--[[ LibDraw.SetColor(255,0,0)
LibDraw.Line(tentCache[i]["tentX"],tentCache[i]["tentY"],tentCache[i]["tentZ"],tentCache[i]["tentX2"],tentCache[i]["tentY2"],tentCache[i]["tentZ"])
]]
@@ -175,7 +177,7 @@ function br.carapaceMath(Unit1, Unit2)
end
end
-function br.getLineOfSight(Unit1, Unit2)
+function misc:getLineOfSight(Unit1, Unit2)
if Unit2 == nil then
Unit2 = Unit1
if Unit2 == "player" then
@@ -184,68 +186,72 @@ function br.getLineOfSight(Unit1, Unit2)
Unit1 = "player"
end
end
+
+ -- First check: units must be visible
+ if not (br.functions.unit:GetObjectExists(Unit1) and br.functions.unit:GetUnitIsVisible(Unit1) and
+ br.functions.unit:GetObjectExists(Unit2) and br.functions.unit:GetUnitIsVisible(Unit2)) then
+ return false
+ end
+
local skipLoSTable = br.lists.los
- if skipLoSTable[br.GetObjectID(Unit1)] or skipLoSTable[br.GetObjectID(Unit2)] or -- Kyrian Hunter Ability
- (Unit1 and Unit1 ~= "player" and br.getDebuffRemain(Unit1, 308498) > 0) or
- (Unit2 and Unit2 ~= "player" and br.getDebuffRemain(Unit2, 308498) > 0)
+ if skipLoSTable[br.functions.unit:GetObjectID(Unit1)] or skipLoSTable[br.functions.unit:GetObjectID(Unit2)] or -- Kyrian Hunter Ability
+ (Unit1 and Unit1 ~= "player" and br.functions.aura:getDebuffRemain(Unit1, 308498) > 0) or
+ (Unit2 and Unit2 ~= "player" and br.functions.aura:getDebuffRemain(Unit2, 308498) > 0)
then
return true
end
- if br.GetObjectExists(Unit1) and br.GetUnitIsVisible(Unit1) and br.GetObjectExists(Unit2) and br.GetUnitIsVisible(Unit2) then
- local X1, Y1, Z1 = br.GetObjectPosition(Unit1)
- local X2, Y2, Z2 = br.GetObjectPosition(Unit2)
- local pX = br.GetObjectPosition("player")
- local flags = bit.bor(0x10, 0x100)
- local trace
- -- Only calculate if we actually got values
- if (X1 == nil or X2 == nil or pX == nil) then return false end
- -- Trace to see if we are in Line of Sight
- if br.player and br.player.eID and (br.player.eID == 2398 or br.player.eID == 2399) then
- trace = br._G.TraceLine(X1, Y1, Z1 + 2 --[[.25]], X2, Y2, Z2 + 2 --[[.25]], 0x100111)
- else
- trace = br._G.TraceLine(X1, Y1, Z1 + 2.25, X2, Y2, Z2 + 2.25, flags)
- -- if (br._G.UnitIsUnit(Unit2,"target")) then
- -- br._G.print("Target Is LoS: "..tostring(trace))
- -- end
- end
- if trace == nil or trace == false then
- --Print("Past Traceline")
- if br.player and br.player.eID and br.player.eID == 2141 then
- if pX < -108 and X2 < -108 then
- return true
- elseif (pX > -108 and pX < -54) and (X2 > -108 and X2 < -54) then
- return true
- elseif pX > -54 and X2 > -54 then
- return true
- else
- return false
- end
- elseif br.player and br.player.eID and br.player.eID == 2337 then
- --Print("Past Cara Check")
- if br.carapaceMath(Unit1, Unit2) == true then
- --Print("Cara True")
- return true
- else
- --Print("cara False")
- return false
- end
+
+ local X1, Y1, Z1 = br.functions.unit:GetObjectPosition(Unit1)
+ local X2, Y2, Z2 = br.functions.unit:GetObjectPosition(Unit2)
+ local pX = br.functions.unit:GetObjectPosition("player")
+ local flags = bit.bor(0x10, 0x100)
+ local trace
+ -- Only calculate if we actually got values
+ if (X1 == nil or X2 == nil or pX == nil) then return false end
+ -- Trace to see if we are in Line of Sight
+ if br.player and br.player.eID and (br.player.eID == 2398 or br.player.eID == 2399) then
+ trace = br._G.TraceLine(X1, Y1, Z1 + 2 --[[.25]], X2, Y2, Z2 + 2 --[[.25]], 0x100111)
+ else
+ trace = br._G.TraceLine(X1, Y1, Z1 + 2.25, X2, Y2, Z2 + 2.25, flags)
+ -- if (br._G.UnitIsUnit(Unit2,"target")) then
+ -- br._G.print("Target Is LoS: "..tostring(trace))
+ -- end
+ end
+ if trace == nil or trace == false then
+ --Print("Past Traceline")
+ if br.player and br.player.eID and br.player.eID == 2141 then
+ if pX < -108 and X2 < -108 then
+ return true
+ elseif (pX > -108 and pX < -54) and (X2 > -108 and X2 < -54) then
+ return true
+ elseif pX > -54 and X2 > -54 then
+ return true
else
- --Print("Skippped all the code")
+ return false
+ end
+ elseif br.player and br.player.eID and br.player.eID == 2337 then
+ --Print("Past Cara Check")
+ if misc:carapaceMath(Unit1, Unit2) == true then
+ --Print("Cara True")
return true
+ else
+ --Print("cara False")
+ return false
end
else
- -- br._G.print("Really Skipped it all")
+ --Print("Skippped all the code")
return true
end
else
- return false
+ -- br._G.print("Really Skipped it all")
+ return true
end
end
-- if getGround("target"[,"target"]) then
-function br.getGround(Unit)
- if br.GetObjectExists(Unit) and br.GetUnitIsVisible(Unit) then
- local X1, Y1, Z1 = br.GetObjectPosition(Unit)
+function misc:getGround(Unit)
+ if br.functions.unit:GetObjectExists(Unit) and br.functions.unit:GetUnitIsVisible(Unit) then
+ local X1, Y1, Z1 = br.functions.unit:GetObjectPosition(Unit)
if br._G.TraceLine(X1, Y1, Z1, X1, Y1, Z1 - 2, 0x10) == nil and br._G.TraceLine(X1, Y1, Z1, X1, Y1, Z1 - 2, 0x100) == nil then
return false
else
@@ -254,9 +260,9 @@ function br.getGround(Unit)
end
end
-function br.getGroundDistance(Unit)
- if br.GetObjectExists(Unit) and br.GetUnitIsVisible(Unit) then
- local X1, Y1, Z1 = br.GetObjectPosition(Unit)
+function misc:getGroundDistance(Unit)
+ if br.functions.unit:GetObjectExists(Unit) and br.functions.unit:GetUnitIsVisible(Unit) then
+ local X1, Y1, Z1 = br.functions.unit:GetObjectPosition(Unit)
for i = 1, 100 do
if br._G.TraceLine(X1, Y1, Z1, X1, Y1, Z1 - i / 10, 0x10) ~= nil or br._G.TraceLine(X1, Y1, Z1, X1, Y1, Z1 - i / 10, 0x100) ~= nil then
return i / 10
@@ -268,10 +274,10 @@ function br.getGroundDistance(Unit)
end
-- if getPetLineOfSight("target"[,"target"]) then
-function br.getPetLineOfSight(Unit)
- if br.GetObjectExists(Unit) and br.GetUnitIsVisible("pet") and br.GetUnitIsVisible(Unit) then
- local X1, Y1, Z1 = br.GetObjectPosition("pet")
- local X2, Y2, Z2 = br.GetObjectPosition(Unit)
+function misc:getPetLineOfSight(Unit)
+ if br.functions.unit:GetObjectExists(Unit) and br.functions.unit:GetUnitIsVisible("pet") and br.functions.unit:GetUnitIsVisible(Unit) then
+ local X1, Y1, Z1 = br.functions.unit:GetObjectPosition("pet")
+ local X2, Y2, Z2 = br.functions.unit:GetObjectPosition(Unit)
if br._G.TraceLine(X1, Y1, Z1 + 2, X2, Y2, Z2 + 2, 0x10) == nil then
return true
else
@@ -283,22 +289,22 @@ function br.getPetLineOfSight(Unit)
end
--- Round
-function br.round2(num, idp)
+function misc:round2(num, idp)
local mult = 10 ^ (idp or 0)
return math.floor(num * mult + 0.5) / mult
end
-- if getTalent(8) == true then
-function br.getTalent(Row, Column, specGroup)
+function misc:getTalent(Row, Column, specGroup)
if specGroup == nil then
- specGroup = br._G.GetActiveSpecGroup()
+ specGroup = br._G.C_SpecializationInfo.GetActiveSpecGroup()
end
local _, _, _, selected = br._G.GetTalentInfo(Row, Column, specGroup)
return selected or false
end
-- if hasEmptySlots() then
-function br.hasEmptySlots()
+function misc:hasEmptySlots()
local openSlots = 0
for i = 0, 4 do --Let's look at each bag
local numBagSlots = C_Container.GetContainerNumSlots(i)
@@ -313,24 +319,14 @@ function br.hasEmptySlots()
end
end
--- if hasGlyph(1234) == true then
-function br.hasGlyph(glyphid)
- for i = 1, 6 do
- if select(4, br._G.GetGlyphSocketInfo(i)) == glyphid or select(6, br._G.GetGlyphSocketInfo(i)) == glyphid then
- return true
- end
- end
- return false
-end
-
-- UnitGUID("target"):sub(-15,-10)
--if isGarrMCd() then
-function br.isGarrMCd(Unit)
+function misc:isGarrMCd(Unit)
if Unit == nil then
Unit = "target"
end
- if br.GetUnitExists(Unit) and (br.UnitDebuffID(Unit, 145832) or br.UnitDebuffID(Unit, 145171) or br.UnitDebuffID(Unit, 145065) or br.UnitDebuffID(Unit, 145071)) then
+ if br.functions.unit:GetUnitExists(Unit) and (br.functions.aura:UnitDebuffID(Unit, 145832) or br.functions.aura:UnitDebuffID(Unit, 145171) or br.functions.aura:UnitDebuffID(Unit, 145065) or br.functions.aura:UnitDebuffID(Unit, 145071)) then
return true
else
return false
@@ -338,66 +334,66 @@ function br.isGarrMCd(Unit)
end
-- if isInCombat("target") then
-function br.isInCombat(Unit)
- if br._G.UnitAffectingCombat(Unit) or br.isChecked("Ignore Combat") then
+function misc:isInCombat(Unit)
+ if br._G.UnitAffectingCombat(Unit) or br.functions.misc:isChecked("Ignore Combat") then
return true
else
return false
end
end
-function br.isInArdenweald()
+function misc:isInArdenweald()
local tContains = br._G.tContains
local mapID = br._G.C_Map.GetBestMapForUnit("player")
return tContains(br.lists.maps.Ardenweald, mapID)
end
-function br.isInBastion()
+function misc:isInBastion()
local tContains = br._G.tContains
local mapID = br._G.C_Map.GetBestMapForUnit("player")
return tContains(br.lists.maps.Bastion, mapID)
end
-function br.isInMaldraxxus()
+function misc:isInMaldraxxus()
local tContains = br._G.tContains
local mapID = br._G.C_Map.GetBestMapForUnit("player")
return tContains(br.lists.maps.Maldraxxus, mapID)
end
-function br.isInRevendreth()
+function misc:isInRevendreth()
local tContains = br._G.tContains
local mapID = br._G.C_Map.GetBestMapForUnit("player")
return tContains(br.lists.maps.Revendreth, mapID)
end
-function br.isInTheMaw()
+function misc:isInTheMaw()
local tContains = br._G.tContains
local mapID = br._G.C_Map.GetBestMapForUnit("player")
return tContains(br.lists.maps.TheMaw, mapID)
end
-- if isInDraenor() then
-function br.isInDraenor()
+function misc:isInDraenor()
local tContains = br._G.tContains
local currentMapID = br._G.C_Map.GetBestMapForUnit("player")
local draenorMapIDs = br.lists.maps.Draenor
return tContains(draenorMapIDs, currentMapID)
end
-function br.isInLegion()
+function misc:isInLegion()
local tContains = br._G.tContains
local currentMapID = br._G.C_Map.GetBestMapForUnit("player")
local legionMapIDs = br.lists.maps.Legion
return tContains(legionMapIDs, currentMapID)
end
-function br.isInProvingGround()
+function misc:isInProvingGround()
local currentMapID = br._G.C_Map.GetBestMapForUnit("player")
return currentMapID == 480
end
-- if IsInPvP() then
-function br.isInPvP()
+function misc:isInPvP()
local inpvp = br._G.GetPVPTimer()
if (inpvp ~= 301000 and inpvp ~= -1) or (br._G.UnitIsPVP("player") and br._G.UnitIsPVP("target")) then
return true
@@ -408,30 +404,32 @@ end
--if isLongTimeCCed("target") then
-- CCs with >=20 seconds
-function br.isLongTimeCCed(Unit)
+function misc:isLongTimeCCed(Unit)
if Unit == nil then
return false
end
local longTimeCC = br.lists.longCC
for i = 1, 40 do
- local debuffSpellID = select(10, br._G.UnitDebuff(Unit, i))
- if debuffSpellID == nil then
- return false
- end
- if longTimeCC[tonumber(debuffSpellID)] == true then
- return true
- end
+ -- local debuffSpellID = select(10, br._G.UnitDebuff(Unit, i))
+ local auraInfo = C_UnitAuras.GetDebuffDataByIndex(Unit, i)
+ if auraInfo and longTimeCC[tonumber(auraInfo.spellId)] == true then return true end
+ -- if debuffSpellID == nil then
+ -- return false
+ -- end
+ -- if longTimeCC[tonumber(debuffSpellID)] == true then
+ -- return true
+ -- end
end
return false
end
-- if isLooting() then
-function br.isLooting()
+function misc:isLooting()
return br._G.GetNumLootItems() > 0
end
-- if not isMoving("target") then
-function br.isMoving(Unit)
+function misc:isMoving(Unit)
if Unit == nil then
return false
end
@@ -439,31 +437,31 @@ function br.isMoving(Unit)
end
-- if IsMovingTime(5) then
-function br.IsMovingTime(time)
+function misc:IsMovingTime(time)
if time == nil then
time = 1
end
if br._G.GetUnitSpeed("player") > 0 then
- if br.IsRunning == nil then
- br.IsRunning = br._G.GetTime()
- br.IsStanding = nil
+ if br.functions.misc.isRunning == nil then
+ br.functions.misc.isRunning = br._G.GetTime()
+ br.functions.misc.isStanding = nil
end
- if br._G.GetTime() - br.IsRunning > time then
+ if br._G.GetTime() - br.functions.misc.isRunning > time then
return true
end
else
- if br.IsStanding == nil then
- br.IsStanding = br._G.GetTime()
- br.IsRunning = nil
+ if br.functions.misc.isStanding == nil then
+ br.functions.misc.isStanding = br._G.GetTime()
+ br.functions.misc.isRunning = nil
end
- if br._G.GetTime() - br.IsStanding > time then
+ if br._G.GetTime() - br.functions.misc.isStanding > time then
return false
end
end
end
-function br.isPlayer(Unit)
- if br.GetUnitExists(Unit) ~= true then
+function misc:isPlayer(Unit)
+ if br.functions.unit:GetUnitExists(Unit) ~= true then
return false
end
if br._G.UnitIsPlayer(Unit) == true then
@@ -483,17 +481,17 @@ function br.isPlayer(Unit)
end
end
-function br.getStandingTime()
- return br.DontMoveStartTime and br._G.GetTime() - br.DontMoveStartTime or nil
+function misc:getStandingTime()
+ return br.readers.common.DontMoveStartTime and br._G.GetTime() - br.readers.common.DontMoveStartTime or nil
end
--
-function br.isStanding(Seconds)
- return br._G.IsFalling() == false and br.DontMoveStartTime and br.getStandingTime() >= Seconds or false
+function misc:isStanding(Seconds)
+ return br._G.IsFalling() == false and br.readers.common.DontMoveStartTime and br.functions.misc:getStandingTime() >= Seconds or false
end
-- if IsStandingTime(5) then
-function br.IsStandingTime(time, unit)
+function misc:IsStandingTime(time, unit)
if time == nil then
time = 1
end
@@ -501,34 +499,34 @@ function br.IsStandingTime(time, unit)
unit = "player"
end
if not br._G.IsFalling() and br._G.GetUnitSpeed(unit) == 0 then
- if br.IsStanding == nil then
- br.IsStanding = br.GetTime()
- br.IsRunning = nil
+ if br.functions.misc.isStanding == nil then
+ br.functions.misc.isStanding = br.GetTime()
+ br.functions.misc.isRunning = nil
end
- if br._G.GetTime() - br.IsStanding > time then
+ if br._G.GetTime() - br.functions.misc.isStanding > time then
return true
end
else
- if br.IsRunning == nil then
- br.IsRunning = br._G.GetTime()
- br.IsStanding = nil
+ if br.functions.misc.isRunning == nil then
+ br.functions.misc.isRunning = br._G.GetTime()
+ br.functions.misc.isStanding = nil
end
- if br._G.GetTime() - br.IsRunning > time then
+ if br._G.GetTime() - br.functions.misc.isRunning > time then
return false
end
end
end
-- if isValidTarget("target") then
-function br.isValidTarget(Unit)
- if br._G.UnitIsEnemy("player", Unit) or br.isDummy(Unit) then
- if br.GetUnitExists(Unit) and not br.GetUnitIsDeadOrGhost(Unit) then
+function misc:isValidTarget(Unit)
+ if br._G.UnitIsEnemy("player", Unit) or br.functions.unit:isDummy(Unit) then
+ if br.functions.unit:GetUnitExists(Unit) and not br.functions.unit:GetUnitIsDeadOrGhost(Unit) then
return true
else
return false
end
else
- if br.GetUnitExists(Unit) then
+ if br.functions.unit:GetUnitExists(Unit) then
return true
else
return false
@@ -536,25 +534,25 @@ function br.isValidTarget(Unit)
end
end
-function br.isTargeting(Unit, MatchUnit)
- if br.GetUnit(Unit) == nil then
+function misc:isTargeting(Unit, MatchUnit)
+ if br.functions.unit:GetUnit(Unit) == nil then
return false
end
- if br._G.UnitTarget(br.GetUnit(Unit)) == nil then
+ if br._G.UnitTarget(br.functions.unit:GetUnit(Unit)) == nil then
return false
end
if MatchUnit == nil then
MatchUnit = "player"
end
- return br._G.UnitTarget(br.GetUnit(Unit)) == br._G.ObjectPointer(MatchUnit)
+ return br._G.UnitTarget(br.functions.unit:GetUnit(Unit)) == br._G.ObjectPointer(MatchUnit)
end
-function br.hasTank()
- if #br.friend == 1 then return false end
- for i = 1, #br.friend do
- local thisUnit = br.friend[i].unit
+function misc:hasTank()
+ if #br.engines.healingEngine.friend == 1 then return false end
+ for i = 1, #br.engines.healingEngine.friend do
+ local thisUnit = br.engines.healingEngine.friend[i].unit
if br._G.UnitGroupRolesAssigned(thisUnit) == "TANK"
- and br.getDistance(thisUnit) < 40 and br._G.UnitIsPlayer(thisUnit)
+ and br.functions.range:getDistance(thisUnit) < 40 and br._G.UnitIsPlayer(thisUnit)
then
return true
end
@@ -562,92 +560,213 @@ function br.hasTank()
return false
end
-function br.enemyListCheck(Unit)
- local targetBuff = 0
- local playerBuff = 0
- if br.UnitDebuffID(Unit, 310499) then
- targetBuff = 1
+function misc:enemyListCheck(Unit)
+ -- local targetBuff = 0
+ -- local playerBuff = 0
+ -- if br.functions.aura:UnitDebuffID(Unit, 310499) then
+ -- targetBuff = 1
+ -- end
+ -- if br.functions.aura:UnitDebuffID("player", 310499) then
+ -- playerBuff = 1
+ -- end
+ -- if targetBuff ~= playerBuff then
+ -- if br.functions.misc:isChecked("Enemy List Debug") then
+ -- br._G.print("[EnemyListCheck] Phase sync failed for " .. tostring(br._G.UnitName(Unit)))
+ -- end
+ -- return false
+ -- end
+ local phaseReason = br._G.UnitInPhase(Unit)--br._G.UnitPhaseReason(Unit)
+ local distance = br.functions.range:getDistance(Unit, "player")
+ local mcCheck = (br.functions.misc:isChecked("Attack MC Targets") and (not br.functions.unit:GetUnitIsFriend(Unit, "player") or br._G.UnitIsCharmed(Unit))) or
+ not br.functions.unit:GetUnitIsFriend(Unit, "player")
+ local inPhase = phaseReason--not phaseReason or phaseReason == 2 or phaseReason == 3
+ if (br.functions.aura:UnitDebuffID("player", 320102) or br.functions.aura:UnitDebuffID(Unit, 424495)) and br._G.UnitIsPlayer(Unit) then
+ return true
end
- if br.UnitDebuffID("player", 310499) then
- playerBuff = 1
+ -- Check if unit was created by player, but allow quest NPCs that turn hostile (like Peak of Serenity monks)
+ -- UnitReaction: 1=Hated, 2=Hostile, 3=Unfriendly, 4=Neutral, 5+=Friendly
+ local createdByPlayer = br._G.UnitCreator(Unit) == br._G.ObjectPointer("player")
+ local unitReaction = br._G.UnitReaction(Unit, "player") or 5
+ local isHostileQuestNPC = createdByPlayer and unitReaction < 4 -- Hostile/Unfriendly reaction
+
+ -- Debug output to identify which check is failing
+ local checks = {
+ exists = br.functions.unit:GetObjectExists(Unit),
+ notDead = not br.functions.unit:GetUnitIsDeadOrGhost(Unit),
+ canAttack = br._G.UnitCanAttack("player", Unit),
+ hasHealth = br._G.UnitHealth(Unit) > 0,
+ inRange = distance < 50,
+ notCritter = not br.functions.unit:isCritter(Unit),
+ mcCheck = mcCheck,
+ notPet = not br.functions.unit:GetUnitIsUnit(Unit, "pet"),
+ creatorCheck = (not createdByPlayer or isHostileQuestNPC),
+ notWaterEle = br.functions.unit:GetObjectID(Unit) ~= 11492,
+ }
+
+ -- Expensive LoS check only done if other checks pass (early exit optimization)
+ local basicChecks = checks.exists and checks.notDead and checks.canAttack and checks.hasHealth
+ and checks.inRange and checks.notCritter and checks.mcCheck and checks.notPet
+ and checks.creatorCheck and checks.notWaterEle
+
+ if not basicChecks then
+ return false -- Early exit before expensive LoS check
+ end
+
+ -- OPTIMIZATION: More aggressive LoS check skipping to reduce FPS impact
+ -- Skip LoS check entirely for:
+ -- 1. Close enemies (<15y) - always reachable
+ -- 2. Units in combat - already engaged
+ -- 3. Mid-range units (15-40y) - perform check to maintain accuracy for prowl/pre-combat detection
+ -- 4. Distant units (>40y) - skip unless damaged or targeting us
+ local isDamagedUnit = br.engines.enemiesEngine.damaged and br.engines.enemiesEngine.damaged[br._G.ObjectPointer(Unit)] ~= nil
+ local isTargetingUs = br.functions.misc:isTargeting(Unit)
+ local los = true -- Default to true (skip check)
+
+ if distance >= 15 and not br._G.UnitAffectingCombat(Unit) then
+ -- CRITICAL: Always check LoS for mid-range units (15-40y) to support pre-emptive prowling/stealth detection
+ -- These checks are needed for Feral pre-emptive prowl, Rogue stealth prep, etc.
+ if distance <= 40 then
+ los = br.functions.misc:getLineOfSight("player", Unit)
+ elseif isDamagedUnit or isTargetingUs then
+ -- Distant but important: damaged or targeting us
+ los = br.functions.misc:getLineOfSight("player", Unit)
+ else
+ -- Very distant (>40y), not damaged, not targeting: skip LoS check, assume no LoS
+ -- These units are too far to matter for gameplay
+ los = false
+ end
end
- if targetBuff ~= playerBuff then
+
+ local allPass = basicChecks and los
+ and ((Unit ~= 131824 and Unit ~= 131823 and Unit ~= 131825) or ((br.functions.aura:UnitBuffID(Unit, 260805)
+ or br.functions.unit:GetUnitIsUnit(Unit, "target")) and (Unit == 131824 or Unit == 131823 or Unit == 131825)))
+
+ -- if not allPass and br.functions.misc:isChecked("Enemy List Debug") then
+ -- local unitName = br._G.UnitName(Unit) or "Unknown"
+ -- br._G.print("[EnemyListCheck] FAILED for " .. unitName .. " (Dist: " .. string.format("%.1f", distance) .. ")")
+ -- for k, v in pairs(checks) do
+ -- if not v then
+ -- br._G.print(" - " .. k .. ": FAILED")
+ -- end
+ -- end
+ -- if not checks.los and not br._G.UnitAffectingCombat(Unit) then
+ -- br._G.print(" - NO LoS AND not in combat")
+ -- end
+ -- end
+
+ return allPass
+end
+
+function misc:isValidUnit(Unit)
+ if Unit == nil then
return false
end
- local phaseReason = br._G.UnitPhaseReason(Unit)
- local distance = br.getDistance(Unit, "player")
- local mcCheck = (br.isChecked("Attack MC Targets") and (not br.GetUnitIsFriend(Unit, "player") or br._G.UnitIsCharmed(Unit))) or
- not br.GetUnitIsFriend(Unit, "player")
- local inPhase = not phaseReason or phaseReason == 2 or phaseReason == 3
- if (br.UnitDebuffID("player", 320102) or br.UnitDebuffID(Unit, 424495)) and br._G.UnitIsPlayer(Unit) then
- return true
+
+ -- PRIORITY: Units in damaged table bypass most validation checks
+ -- These are units actively in combat with our group (attacking or being attacked)
+ -- The combatlog cleanup already ensures these units are valid (exists, alive, attackable)
+ -- damaged table keys are pointers from GetObjectWithGUID()
+ -- damaged table values are unitSetup objects where .unit is also the pointer
+ -- HOWEVER, we still need to check line of sight to prevent casting on units behind walls
+ if br.engines.enemiesEngine.damaged then
+ local unitPointer = br._G.ObjectPointer(Unit)
+ -- Direct pointer lookup - ObjectPointer() should match GetObjectWithGUID() for same unit
+ if unitPointer and br.engines.enemiesEngine.damaged[unitPointer] ~= nil then
+ return true
+ end
end
- return br.GetObjectExists(Unit) and not br.GetUnitIsDeadOrGhost(Unit) and inPhase and
- br._G.UnitCanAttack("player", Unit) and br._G.UnitHealth(Unit) > 0 and distance < 50 and
- not br.isCritter(Unit) and
- mcCheck and
- not br.GetUnitIsUnit(Unit, "pet") and
- br._G.UnitCreator(Unit) ~= br._G.ObjectPointer("player") and
- br.GetObjectID(Unit) ~= 11492 and
- br.getLineOfSight("player", Unit) and
- ((Unit ~= 131824 and Unit ~= 131823 and Unit ~= 131825) or
- ((br.UnitBuffID(Unit, 260805) or br.GetUnitIsUnit(Unit, "target")) and (Unit == 131824 or Unit == 131823 or Unit == 131825)))
-end
-function br.isValidUnit(Unit)
- local inInstance = br._G.IsInInstance()
- local hostileOnly = br.isChecked("Hostiles Only")
- local playerTarget = br.GetUnitIsUnit(Unit, "target")
- local reaction = br.GetUnitReaction(Unit, "player") or 10
- local targeting = br.isTargeting(Unit)
- local dummy = br.isDummy(Unit)
- local burnUnit = br.getOptionCheck("Forced Burn") and br.isBurnTarget(Unit) > 0
- local isCC = br.getOptionCheck("Don't break CCs") and br.isLongTimeCCed(Unit) or false
- local mcCheck = (br.isChecked("Attack MC Targets") and (not br.GetUnitIsFriend(Unit, "player") or (br._G.UnitIsCharmed(Unit) and br._G.UnitCanAttack("player", Unit))))
- or not br.GetUnitIsFriend(Unit, "player")
- if playerTarget and (br.UnitDebuffID("player", 320102) or br.UnitDebuffID(Unit, 424495)) and br._G.UnitIsPlayer(Unit) then
+ -- Calculate all conditions once
+ local unitPointer = br._G.ObjectPointer(Unit)
+ local hostileOnly = br.functions.misc:isChecked("Hostiles Only")
+ local playerTarget = br.functions.unit:GetUnitIsUnit(Unit, "target")
+ local reaction = br.functions.unit:GetUnitReaction(Unit, "player") or 10
+ local targeting = br.functions.misc:isTargeting(Unit)
+ -- Dummies have special handling:
+ -- They should only be treated as "valid" when they are your current target OR
+ -- when you have a dummy targeted and this unit is within 8 yards of that targeted dummy.
+ -- Otherwise, dummies follow the standard validation path (and generally won't be considered valid without threat/targeting).
+ local isDummy = br.functions.unit:isDummy(Unit)
+ local dummyEligible = false
+ if isDummy then
+ if playerTarget then
+ dummyEligible = true
+ else
+ -- Nearby-dummy allowance for cleave testing: allow other dummies near the targeted dummy.
+ if br.functions.unit:GetObjectExists("target") and br.functions.unit:isDummy("target") then
+ local distToTarget = br.functions.range:getDistance(Unit, "target")
+ if distToTarget ~= nil and distToTarget <= 8 then
+ dummyEligible = true
+ end
+ end
+ end
+ end
+ local burnUnit = br.functions.misc:getOptionCheck("Forced Burn") and br.engines.enemiesEngineFunctions:isBurnTarget(Unit) > 0
+ local isCC = br.functions.misc:getOptionCheck("Don't break CCs") and br.functions.misc:isLongTimeCCed(Unit) or false
+ local mcCheck = (br.functions.misc:isChecked("Attack MC Targets") and (not br.functions.unit:GetUnitIsFriend(Unit, "player") or (br._G.UnitIsCharmed(Unit) and br._G.UnitCanAttack("player", Unit))))
+ or not br.functions.unit:GetUnitIsFriend(Unit, "player")
+
+ -- Early exit for PvP debuffs (special case)
+ if playerTarget and (br.functions.aura:UnitDebuffID("player", 320102) or br.functions.aura:UnitDebuffID(Unit, 424495)) and br._G.UnitIsPlayer(Unit) then
return true
end
- if playerTarget and br.units[br._G.UnitTarget("player")] == nil and not br.enemyListCheck("target") then
- return false
+
+ -- Check if unit is tapped but we've damaged it
+ local isTapped = br._G.UnitIsTapDenied(Unit)
+ local hasDamagedTapped = isTapped and br.engines.enemiesEngine.damaged[unitPointer] ~= nil
+
+ -- Check if unit actually passed enemyListCheck or is in units table
+ -- If it's the player target but not in units table, verify it at least passes basic enemyListCheck
+ -- Also allow units that have threat on the group (targeting tank/party) even if not in units table yet
+ local inUnitsTable = br.engines.enemiesEngine.units[Unit] ~= nil
+ local hasThreat = br.functions.combat:hasThreat(Unit)
+ local passedEnemyCheck = inUnitsTable or (playerTarget and br.functions.misc:enemyListCheck(Unit)) or burnUnit or dummyEligible or hasThreat
+
+ -- Fast path for special units (dummy/burn) - skip most checks
+ if passedEnemyCheck and mcCheck and not isCC and (dummyEligible or burnUnit) then
+ return true
end
- if not br.pause(true) and Unit ~= nil and (br.units[Unit] ~= nil or Unit == "target" or burnUnit or dummy) and mcCheck
- and not isCC and (dummy or burnUnit or (not br._G.UnitIsTapDenied(Unit) and br.isSafeToAttack(Unit)
+
+ -- Standard validation path
+ if passedEnemyCheck and mcCheck and not isCC
+ and (hasDamagedTapped or (not isTapped and br.engines.enemiesEngineFunctions:isSafeToAttack(Unit)
and ((hostileOnly and reaction < 4) or (not hostileOnly and reaction < 5) or playerTarget or targeting)))
then
- return (playerTarget and (not inInstance or (inInstance and (#br.friend == 1 or not br.hasTank())))) or targeting or
- burnUnit or br.isInProvingGround() or br.hasThreat(Unit)
+ -- Valid if any threat condition is met:
+ -- Player target, unit targeting us, burn target, proving grounds, or has group threat
+ return playerTarget or targeting or burnUnit or br.functions.misc:isInProvingGround() or hasThreat
end
+
return false
end
-function br.SpecificToggle(toggle)
+function misc:SpecificToggle(toggle)
if br.customToggle then
return false
- elseif br.getOptionValue(toggle) == 1 then
+ elseif br.functions.misc:getOptionValue(toggle) == 1 then
return br._G.IsLeftControlKeyDown()
- elseif br.getOptionValue(toggle) == 2 then
+ elseif br.functions.misc:getOptionValue(toggle) == 2 then
return br._G.IsLeftShiftKeyDown()
- elseif br.getOptionValue(toggle) == 3 then
+ elseif br.functions.misc:getOptionValue(toggle) == 3 then
return br._G.IsRightControlKeyDown()
- elseif br.getOptionValue(toggle) == 4 then
+ elseif br.functions.misc:getOptionValue(toggle) == 4 then
return br._G.IsRightShiftKeyDown()
- elseif br.getOptionValue(toggle) == 5 then
+ elseif br.functions.misc:getOptionValue(toggle) == 5 then
return br._G.IsRightAltKeyDown()
- elseif br.getOptionValue(toggle) == 6 then
+ elseif br.functions.misc:getOptionValue(toggle) == 6 then
return false
- elseif br.getOptionValue(toggle) == 7 then
+ elseif br.functions.misc:getOptionValue(toggle) == 7 then
return br._G.GetKeyState(0x04)
- elseif br.getOptionValue(toggle) == 8 then
+ elseif br.functions.misc:getOptionValue(toggle) == 8 then
return br._G.GetKeyState(0x05)
- elseif br.getOptionValue(toggle) == 9 then
+ elseif br.functions.misc:getOptionValue(toggle) == 9 then
return br._G.GetKeyState(0x06)
- elseif br.getOptionValue(toggle) == 10 then
+ elseif br.functions.misc:getOptionValue(toggle) == 10 then
return br._G.GetKeyState(0xC0)
end
end
-function br.UpdateToggle(toggle, delay)
+function misc:UpdateToggle(toggle, delay)
--if toggle == nil then toggle = "toggle" end
if br.customToggle then
toggle = br.toggleKey
@@ -655,13 +774,13 @@ function br.UpdateToggle(toggle, delay)
if br._G[toggle .. "Timer"] == nil then
br._G[toggle .. "Timer"] = 0
end
- if (br.SpecificToggle(toggle .. " Mode") or br.customToggle) and not br._G.GetCurrentKeyBoardFocus() and br._G.GetTime() - br._G[toggle .. "Timer"] > delay then
+ if (br.functions.misc:SpecificToggle(toggle .. " Mode") or br.customToggle) and not br._G.GetCurrentKeyBoardFocus() and br._G.GetTime() - br._G[toggle .. "Timer"] > delay then
br._G[toggle .. "Timer"] = br._G.GetTime()
- br.UpdateButton(tostring(toggle))
+ br.ui:UpdateButton(tostring(toggle))
end
end
-function br.BurstToggle(toggle, delay)
+function misc:BurstToggle(toggle, delay)
if br.burstKey == nil then
br.burstKey = false
end
@@ -682,16 +801,12 @@ end
-- if pause() then
-- set skipCastingCheck to true, to not check if player is casting
-- (useful if you want to use off-cd stuff, or spells which can be cast while other is casting)
-function br.pause(skipCastingCheck)
- local food = { 257427, 225737, 274914, 192001, 167152, 314646, 308433 }
- local eating = false
+misc.pauseCast = br._G.GetTime()
+function misc:pause(skipCastingCheck)
+ local eating = br.functions.unit:isEating() or false
+ local drinking = br.functions.unit:isDrinking() or false
local pausekey
- for i = 1, #food do
- if br.UnitBuffID("player", food[i]) then
- eating = true
- break
- end
- end
+
-- local button = CreateFrame("Button", "DismountButton")
-- if button == "RightButton" then
-- Print("Right Clicked")
@@ -701,49 +816,49 @@ function br.pause(skipCastingCheck)
if br.disableControl == true then
return true
end
- if br.SpecificToggle("Pause Mode") == nil or br.getValue("Pause Mode") == 6 then
+ if br.functions.misc:SpecificToggle("Pause Mode") == nil or br.functions.misc:getValue("Pause Mode") == 6 then
pausekey = br._G.IsLeftAltKeyDown()
else
- pausekey = br.SpecificToggle("Pause Mode")
+ pausekey = br.functions.misc:SpecificToggle("Pause Mode")
end
-- Focused Azerite Beam / Cyclotronic Blast / Azshara's Font of Power
if not skipCastingCheck then
- local lastCast = br.lastCastTable.tracker[1]
- if br.pauseCast - br._G.GetTime() <= 0 then
+ local lastCast = br.functions.lastCast.lastCastTable.tracker[1]
+ if misc.pauseCast - br._G.GetTime() <= 0 then
local hasted = (1 - br._G.UnitSpellHaste("player") / 100)
-- Focused Azerite Beam
- if lastCast == 295258 and br.getSpellCD(295258) == 0 then
- br.pauseCast = br._G.GetTime() + br.getCastTime(295258) + (br.getCastTime(295261) * hasted)
+ if lastCast == 295258 and br.functions.spell:getSpellCD(295258) == 0 then
+ misc.pauseCast = br._G.GetTime() + br.functions.cast:getCastTime(295258) + (br.functions.cast:getCastTime(295261) * hasted)
end
-- Cyclotronic Blast
- if lastCast == 293491 and br._G.GetItemCooldown(167555) == 0 then
- br.pauseCast = br._G.GetTime() + br.getCastTime(293491) + (2.5 * hasted) + br.getGlobalCD(true)
+ if lastCast == 293491 and br._G.C_Container.GetItemCooldown(167555) == 0 then
+ misc.pauseCast = br._G.GetTime() + br.functions.cast:getCastTime(293491) + (2.5 * hasted) + br.functions.spell:getGlobalCD(true)
end
-- Azshara's Font of Power - Latent Arcana
- if lastCast == 296962 and br._G.GetItemCooldown(169314) == 0 then
- br.pauseCast = br._G.GetTime() + br.getCastTime(296962) + (2.5 * hasted)
+ if lastCast == 296962 and br._G.C_Container.GetItemCooldown(169314) == 0 then
+ misc.pauseCast = br._G.GetTime() + br.functions.cast:getCastTime(296962) + (2.5 * hasted)
end
end
- if br._G.GetTime() < br.pauseCast then
+ if br._G.GetTime() < misc.pauseCast then
return true
- elseif br._G.GetTime() >= br.pauseCast then
- br.pauseCast = br._G.GetTime()
+ elseif br._G.GetTime() >= misc.pauseCast then
+ misc.pauseCast = br._G.GetTime()
end
end
-- DPS Testing
- if br.isChecked("DPS Testing") then
- if br.GetObjectExists("target") and br.isInCombat("player") then
- if br.getCombatTime() >= (tonumber(br.getOptionValue("DPS Testing")) * 60) and br.isDummy() then
+ if br.functions.misc:isChecked("DPS Testing") then
+ if br.functions.unit:GetObjectExists("target") and br.functions.misc:isInCombat("player") then
+ if br.functions.combat:getCombatTime() >= (tonumber(br.functions.misc:getOptionValue("DPS Testing")) * 60) and br.functions.unit:isDummy() then
br._G.StopAttack()
br._G.ClearTarget()
- br._G.print(tonumber(br.getOptionValue("DPS Testing")) ..
+ br._G.print(tonumber(br.functions.misc:getOptionValue("DPS Testing")) ..
" Minute Dummy Test Concluded - Profile Stopped")
br.profileStop = true
else
br.profileStop = false
end
- elseif not br.isInCombat("player") and br.profileStop == true then
- if br.GetObjectExists("target") then
+ elseif not br.functions.misc:isInCombat("player") and br.profileStop == true then
+ if br.functions.unit:GetObjectExists("target") then
br._G.StopAttack()
br._G.ClearTarget()
br.profileStop = false
@@ -751,22 +866,22 @@ function br.pause(skipCastingCheck)
end
end
-- Pause Toggle
- if br.data.settings[br.selectedSpec].toggles ~= nil and br.data.settings[br.selectedSpec].toggles["Pause"] == 1 then
- br.ChatOverlay("\124cFFED0000 -- Paused -- ")
+ if br.data.settings[br.loader.selectedSpec].toggles ~= nil and br.data.settings[br.loader.selectedSpec].toggles["Pause"] == 1 then
+ br.ui.chatOverlay:Show("\124cFFED0000 -- Paused -- ")
return true
end
-- Pause Hold/Auto
- if (pausekey and br._G.GetCurrentKeyBoardFocus() == nil and br.isChecked("Pause Mode")) or br.profileStop
- or (br._G.IsMounted() or br._G.IsFlying() or br._G.UnitOnTaxi("player")
- or (br._G.UnitInVehicle("player") and not br.isChecked("Bypass Vehicle Check")
+ if (pausekey and br._G.GetCurrentKeyBoardFocus() == nil and br.functions.misc:isChecked("Pause Mode")) or br.profileStop
+ or (((br._G.IsMounted() or br._G.IsFlying()) and not br.functions.unit:isBoss("target")) or br._G.UnitOnTaxi("player")
+ or (br._G.UnitInVehicle("player") and not br.functions.misc:isChecked("Bypass Vehicle Check")
and (not br._G.UnitExists("target") or (br._G.UnitExists("target") and not br._G.UnitCanAttack("player", "target"))))
- and not (br.UnitBuffID("player", 190784) or br.UnitBuffID("player", 164222) or br.UnitBuffID("player", 165803) or br.UnitBuffID("player", 157059)))
+ and not (br.functions.aura:UnitBuffID("player", 190784) or br.functions.aura:UnitBuffID("player", 164222) or br.functions.aura:UnitBuffID("player", 165803) or br.functions.aura:UnitBuffID("player", 157059))) -- Draenor Nagrand Mounts
or br._G.SpellIsTargeting() or (br._G.UnitCastingInfo("player") and not skipCastingCheck)
or (br._G.UnitChannelInfo("player") and not skipCastingCheck)
- or br._G.UnitIsDeadOrGhost("player") or eating or br.UnitDebuffID("player", 252753) or -- Potion of Replenishment (BFA Mana channel) Apparently a debuff
- br.UnitBuffID("player", 114018)
+ or br._G.UnitIsDeadOrGhost("player") or eating or drinking or br.functions.aura:UnitDebuffID("player", 252753) or -- Potion of Replenishment (BFA Mana channel) Apparently a debuff
+ br.functions.aura:UnitBuffID("player", 114018) -- Shroud of Concealment (Rogue Subterfuge)
then
- if br.empowerID ~= nil and br.empowerID > 0 and not (pausekey and br._G.GetCurrentKeyBoardFocus() == nil and br.isChecked("Pause Mode")) then
+ if br.empowerID ~= nil and br.empowerID > 0 and not (pausekey and br._G.GetCurrentKeyBoardFocus() == nil and br.functions.misc:isChecked("Pause Mode")) then
return false
elseif (br._G.UnitCastingInfo("player") and not skipCastingCheck) then
local _, _, _, _, endTime = br._G.UnitCastingInfo("player")
@@ -777,7 +892,7 @@ function br.pause(skipCastingCheck)
elseif (br._G.UnitChannelInfo("player") and not skipCastingCheck) then
return true
else
- br.ChatOverlay("Profile Paused")
+ br.ui.chatOverlay:Show("Profile Paused")
return true
end
else
@@ -786,7 +901,7 @@ function br.pause(skipCastingCheck)
end
-- feed a var
-function br.toggleTrueNil(var)
+function misc:toggleTrueNil(var)
if _G[var] ~= true then
_G[var] = true
else
@@ -794,9 +909,9 @@ function br.toggleTrueNil(var)
end
end
-function br.spellDebug(Message)
- if br.imDebugging == true and br.getOptionCheck("Debugging Mode") then
- br.ChatOverlay(Message)
+function misc:spellDebug(Message)
+ if br.imDebugging == true and br.functions.misc:getOptionCheck("Debugging Mode") then
+ br.ui.chatOverlay:Show(Message)
end
end
@@ -827,6 +942,83 @@ local addFindings = function(thisOption, thisPage, thisTimes)
-- reportFindings[thisOption].page = thisPage
-- reportFindings[thisOption].timesFound = thisTimes
end
+-- lightweight per-tick cache to avoid repeated heavy searches that can cause "script ran too long"
+-- local findOptionCache = {}
+-- local findOptionCacheTime = 0
+
+-- local function findOption(Value, Page, Type)
+-- -- clear cache each ~frame (small delta) to avoid stale UI changes while still preventing hot-loop reevaluation
+-- local now = br._G.GetTime()
+-- if now - findOptionCacheTime > 0.05 then
+-- findOptionCache = {}
+-- findOptionCacheTime = now
+-- end
+
+-- local key = tostring(Value) .. "|" .. tostring(Page or "") .. "|" .. tostring(Type or "")
+-- if findOptionCache[key] ~= nil then
+-- return findOptionCache[key]
+-- end
+
+-- -- Assume we are checking Rotation Options, if not specified.
+-- if Page == nil then
+-- Page = "Rotation Options"
+-- end
+-- if Type == nil then Type = " Check" end
+
+-- if br.data and br.data.settings then
+-- if br.data.settings[br.loader.selectedSpec] and br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile] then
+-- local settings = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]
+-- local option = Value .. Type
+-- -- Attempt to find the option using the provided Page (or default page: "Rotation")
+-- if settings[Page] and settings[Page][option] ~= nil then
+-- local res = settings[Page][option] -- Found requested option
+-- findOptionCache[key] = res
+-- return res
+-- end
+-- -- If no Page was provided, and not in the default location, look for it.
+-- local timesFound = 0
+-- local foundOption
+-- if settings and settings["PageList"] then
+-- for i = 1, #settings["PageList"] do
+-- local thisPage = settings["PageList"][i]
+-- if thisPage and settings[thisPage] and settings[thisPage][option] ~= nil then
+-- timesFound = timesFound + 1
+-- foundOption = settings[thisPage][option]
+-- addFindings(option, thisPage, timesFound)
+-- end
+-- end
+-- -- br.report = reportFindings
+-- if reportFindings[option] and #reportFindings[option].Findings ~= 1 and not reportFindings[option].reported then
+-- if #reportFindings[option].Findings == 0 then
+-- br._G.print("No option found for: " .. tostring(option))
+-- reportFindings[option].reported = true
+-- end
+-- if #reportFindings[option].Findings > 1 then
+-- for i = 1, #reportFindings[option].Findings do
+-- local report = reportFindings[option].Findings[i]
+-- br._G.print("Found Option: " ..
+-- tostring(report.option) ..
+-- " on Page: " ..
+-- tostring(report.page))
+-- end
+-- reportFindings[option].reported = true
+-- end
+-- elseif reportFindings[option] ~= nil and reportFindings[option].Findings ~= nil and #reportFindings[option].Findings == 1 then
+-- local thisOption = reportFindings[option].Findings[1]
+-- if settings and settings[thisOption.page] and settings[thisOption.page][thisOption.option] then
+-- local res = settings[thisOption.page][thisOption.option]
+-- findOptionCache[key] = res
+-- return res
+-- end
+-- end
+-- end
+-- end
+-- end
+
+-- local final = (Type == " Check") and false or 0
+-- findOptionCache[key] = final
+-- return final
+-- end
local function findOption(Value, Page, Type)
-- Assume we are checking Rotation Options, if not specified.
if Page == nil then
@@ -838,9 +1030,13 @@ local function findOption(Value, Page, Type)
-- print("Looking in: " .. tostring(Page))
if Type == nil then Type = " Check" end
if br.data and br.data.settings then
- if br.data.settings[br.selectedSpec] and br.data.settings[br.selectedSpec][br.selectedProfile] then
- local settings = br.data.settings[br.selectedSpec][br.selectedProfile]
- local option = Value .. Type
+ if br.data.settings[br.loader.selectedSpec] and br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile] then
+ local settings = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]
+ -- Defensive: if Value is nil, return default based on Type to avoid concatenation errors
+ if Value == nil then
+ if Type == " Check" then return false else return 0 end
+ end
+ local option = Value .. Type
-- Attempt to find the option using the provided Page (or default page: "Rotation")
if settings[Page] and settings[Page][option] ~= nil then
-- print("Found Option: \"" .. tostring(option) .. "\" within Page: \"" .. tostring(Page) .. "\"")
@@ -849,11 +1045,11 @@ local function findOption(Value, Page, Type)
-- If no Page was provided, and not in the default location, look for it.
local timesFound = 0
local foundOption
- if settings["PageList"] then
+ if settings and settings["PageList"] then
for i = 1, #settings["PageList"] do
local thisPage = settings["PageList"][i]
-- print("Searching on Page: " .. tostring(thisPage) .. " for Option: " .. tostring(option))
- if settings[thisPage] and settings[thisPage][option] ~= nil then
+ if thisPage and settings[thisPage] and settings[thisPage][option] ~= nil then
-- print("Found Option: \"" .. tostring(option) .. "\" within Page: \"" .. tostring(thisPage) .. "\"")
timesFound = timesFound + 1
foundOption = settings[thisPage][option] -- Found requested option
@@ -892,31 +1088,32 @@ local function findOption(Value, Page, Type)
end
end
--- if br.isChecked("Debug") then
-function br.isChecked(Value, Page)
+-- if br.functions.misc:isChecked("Debug") then
+function misc:isChecked(Value, Page)
+ if Value == nil then return false end
return findOption(Value, Page, " Check")
end
--- if br.isSelected("Stormlash Totem") then
-function br.isSelected(Value)
+-- if br.functions.misc:isSelected("Stormlash Totem") then
+function misc:isSelected(Value)
if br.data.settings ~= nil
- and (br.data.settings[br.selectedSpec].toggles["Cooldowns"] == 3
- or (br.isChecked(Value) and (br.getValue(Value) == 3
- or (br.getValue(Value) == 2 and br.data.settings[br.selectedSpec].toggles["Cooldowns"] == 2))))
+ and (br.data.settings[br.loader.selectedSpec].toggles["Cooldowns"] == 3
+ or (br.functions.misc:isChecked(Value) and (br.functions.misc:getValue(Value) == 3
+ or (br.functions.misc:getValue(Value) == 2 and br.data.settings[br.loader.selectedSpec].toggles["Cooldowns"] == 2))))
then
return true
end
return false
end
--- if br.getValue("player") <= br.getValue("Eternal Flame") then
--- function br.getValue(Value)
+-- if br.functions.misc:getValue("player") <= br.functions.misc:getValue("Eternal Flame") then
+-- function misc:getValue(Value)
-- if br.data~=nil then
--- if br.data.settings[br.selectedSpec][br.selectedProfile]~=nil then
--- if br.data.settings[br.selectedSpec][br.selectedProfile][Value.."Status"] ~= nil then
--- return br.data.settings[br.selectedSpec][br.selectedProfile][Value.."Status"]
--- elseif br.data.settings[br.selectedSpec][br.selectedProfile][Value.."Drop"] ~= nil then
--- return br.data.settings[br.selectedSpec][br.selectedProfile][Value.."Drop"]
+-- if br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]~=nil then
+-- if br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile][Value.."Status"] ~= nil then
+-- return br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile][Value.."Status"]
+-- elseif br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile][Value.."Drop"] ~= nil then
+-- return br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile][Value.."Drop"]
-- else
-- return 0
-- end
@@ -925,7 +1122,7 @@ end
-- return 0
-- end
-- end
-function br.getValue(Value, Page)
+function misc:getValue(Value, Page)
local statusValue = findOption(Value, Page, " Status")
if statusValue and statusValue > 0 then
return statusValue
@@ -939,7 +1136,7 @@ function br.getValue(Value, Page)
return editBoxValue
end
-- if br.data ~= nil and br.data.settings ~= nil then
- -- local selectedProfile = br.data.settings[br.selectedSpec][br.selectedProfile]
+ -- local selectedProfile = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]
-- if selectedProfile ~= nil then
-- if selectedProfile[Value .. "EditBox"] ~= nil then
-- return selectedProfile[Value .. "EditBox"]
@@ -948,7 +1145,7 @@ function br.getValue(Value, Page)
-- end
return 0
-- if br.data ~= nil and br.data.settings ~= nil then
- -- local selectedProfile = br.data.settings[br.selectedSpec][br.selectedProfile]
+ -- local selectedProfile = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]
-- if selectedProfile ~= nil then
-- if selectedProfile[Value .. "Status"] ~= nil then
-- return selectedProfile[Value .. "Status"]
@@ -965,7 +1162,7 @@ function br.getValue(Value, Page)
-- end
end
-function br.setValue(Value, Page, Amount)
+function misc:setValue(Value, Page, Amount)
local statusValue = findOption(Value, Page, " Status")
if statusValue and statusValue > 0 then
statusValue:SetNumber(Amount)
@@ -981,21 +1178,21 @@ function br.setValue(Value, Page, Amount)
end
-- used to gather informations from the bot options frame
-function br.getOptionCheck(Value)
- return br.isChecked(Value)
+function misc:getOptionCheck(Value)
+ return br.functions.misc:isChecked(Value)
end
-function br.getOptionValue(Value)
- return br.getValue(Value)
+function misc:getOptionValue(Value)
+ return br.functions.misc:getValue(Value)
end
-function br.setOptionValue(Value, Amount)
- return br.setValue(Value, nil, Amount)
+function misc:setOptionValue(Value, Amount)
+ return br.functions.misc:setValue(Value, nil, Amount)
end
-function br.getOptionText(Value)
+function misc:getOptionText(Value)
if br.data ~= nil and br.data.settings ~= nil then
- local selectedProfile = br.data.settings[br.selectedSpec][br.selectedProfile]
+ local selectedProfile = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]
if selectedProfile ~= nil then
if selectedProfile[Value .. "Data"] ~= nil then
if selectedProfile[Value .. "Drop"] ~= nil then
@@ -1009,7 +1206,7 @@ function br.getOptionText(Value)
return ""
end
-function br.convertName(name)
+function misc:convertName(name)
if name ~= nil then
-- Remove hyphens and lowercase the letter that follows
name = name:gsub("-(%a)", function(letter)
@@ -1028,22 +1225,28 @@ function br.convertName(name)
return "None"
end
-function br.devMode()
- if br.isChecked("Dev Mode") then
- _G.br = br
+function misc:devMode()
+ if br.functions.misc:isChecked("Dev Mode") then
+ if _G.br == nil then
+ _G.br = br
+ br._G.print("")
+ end
else
- _G.br = nil
+ if _G.br ~= nil then
+ _G.br = nil
+ br._G.print("")
+ end
end
end
-function br.bossHPLimit(unit, hp)
+function misc:bossHPLimit(unit, hp)
-- Boss Active/Health Max
local bossHPMax = 0
local inBossFight = false
- local enemyList = br.getEnemies("player", 40)
+ local enemyList = br.engines.enemiesEngineFunctions:getEnemies("player", 40)
for i = 1, #enemyList do
local thisUnit = enemyList[i]
- if br.isBoss(thisUnit) then
+ if br.functions.unit:isBoss(thisUnit) then
bossHPMax = br._G.UnitHealthMax(thisUnit)
inBossFight = true
break
@@ -1052,7 +1255,7 @@ function br.bossHPLimit(unit, hp)
return (not inBossFight or (inBossFight and br._G.UnitHealthMax(unit) > bossHPMax * (hp / 100)))
end
-function br.talentAnywhere()
+function misc:talentAnywhere()
local removeTalent = br._G.RemoveTalent
local learnTalent = br._G.LearnTalent
-- Load Talent UI if not opened before
@@ -1083,9 +1286,9 @@ function br.talentAnywhere()
for row = 1, 7 do
br.selectedTalent, br.newTalent, br.selectedNew = talentSelection(row)
end
- --br.ChatOverlay(tostring(selectedTalent).." | "..tostring(newTalent).." | "..tostring(selectedNew))
+ --br.ui.chatOverlay:Show(tostring(selectedTalent).." | "..tostring(newTalent).." | "..tostring(selectedNew))
if br.newTalent ~= nil then
- if br.selectedTalent ~= nil and br.selectedTalent ~= br.newTalent and not br.selectedNew and br.timer:useTimer("RemoveTalent", 0.1) then
+ if br.selectedTalent ~= nil and br.selectedTalent ~= br.newTalent and not br.selectedNew and br.debug.timer:useTimer("RemoveTalent", 0.1) then
removeTalent(br.selectedTalent)
end
if br.selectedTalent == nil and br.selectedTalent ~= br.newTalent and not br.selectedNew then
@@ -1100,13 +1303,13 @@ function br.talentAnywhere()
end
end
-function br.getEssenceRank(essenceName)
- if br._G.GetSpellInfo(essenceName) == nil then
+function misc:getEssenceRank(essenceName)
+ if br.api.wow.GetSpellInfo(essenceName) == nil then
return 0
end
local essenceRank = 0
local essenceTable = br._G.C_AzeriteEssence.GetMilestones()
- local icon = select(3, br._G.GetSpellInfo(essenceName))
+ local icon = select(3, br.api.wow.GetSpellInfo(essenceName))
for i = 1, #essenceTable do
local milestone = essenceTable[i]
if milestone.slot ~= nil and milestone.unlocked == true then
@@ -1122,40 +1325,51 @@ function br.getEssenceRank(essenceName)
end
end
-function br.addonDebug(msg, system)
+function misc:addonDebug(msg, system)
if msg == nil then
return
end
- if br.isChecked("Addon Debug Messages") then
- if system == true and (br.getValue("Addon Debug Messages") == 1 or br.getValue("Addon Debug Messages") == 3) then
- if br.timer:useTimer("System Delay", 0.1) then
- print(br.classColor .. "[BadRotations] System Debug: |cffFFFFFF" .. tostring(msg))
- end
- elseif system ~= true and (br.getValue("Addon Debug Messages") == 2 or br.getValue("Addon Debug Messages") == 3) then
- if br.timer:useTimer("Profile Delay", 0.1) then
- print(br.classColor .. "[BadRotations] Profile Debug: |cffFFFFFF" .. tostring(msg))
+ local msgText = tostring(msg)
+ -- Prefer the dropdown value for Addon Debug Messages. Some configs
+ -- may not include a separate checkbox, so rely on the dropdown being
+ -- set.
+ local addonDebugValue = br.functions.misc:getValue("Addon Debug Messages")
+ if br.functions.misc:isChecked("Addon Debug Messages") and addonDebugValue then
+ local showSystem = addonDebugValue == 1 or addonDebugValue == 2
+ local showProfile = addonDebugValue == 1 or addonDebugValue == 3
+
+ -- Use a per-message short timer key so different messages printed
+ -- within a short interval aren't suppressed by the same global key.
+ local msgKey = msgText
+ if #msgKey > 40 then msgKey = msgKey:sub(1, 40) end
+
+ -- Immediate-print for casting-related messages to avoid them being
+ -- swallowed by simultaneous 'Combat Started' messages. Use a
+ -- short per-message guard so we don't spam identical lines.
+ -- Respect configured channel filtering (System/Profile/All).
+ if showProfile and system ~= true and string.find(msgText, "Casting") then
+ local castKey = "Casting Immediate:" .. msgKey
+ if br.debug.timer:useTimer(castKey, 0.05) then
+ print(br.ui.colors.class .. "[BadRotations] Profile Debug: |cffFFFFFF" .. msgText)
end
+ return
end
- end
-end
-function br.store(key, value)
- if br.profile == nil then
- br.profile = {}
- end
- br.profile[key] = value
- return true
-end
-
-function br.fetch(key, default)
- if br.profile == nil then
- br.profile = {}
+ if system == true and showSystem then
+ local timerKey = "System Delay:" .. msgKey
+ if br.debug.timer:useTimer(timerKey, 0.1) then
+ print(br.ui.colors.class .. "[BadRotations] System Debug: |cffFFFFFF" .. msgText)
+ end
+ elseif system ~= true and showProfile then
+ local timerKey = "Profile Delay:" .. msgKey
+ if br.debug.timer:useTimer(timerKey, 0.1) then
+ print(br.ui.colors.class .. "[BadRotations] Profile Debug: |cffFFFFFF" .. msgText)
+ end
+ end
end
- local value = br.profile[key]
- return value == nil and default or value
end
-function br.sanguineCheck(unit)
+function misc:sanguineCheck(unit)
if br.sanguine then
local x, y, z = br._G.ObjectPosition(unit)
for _, v in pairs(br.sanguine) do
@@ -1167,7 +1381,7 @@ function br.sanguineCheck(unit)
return false
end
-function br.isTableEmpty(table)
+function misc:isTableEmpty(table)
if table == nil then
return true
end
@@ -1178,6 +1392,19 @@ function br.isTableEmpty(table)
return retval
end
-function br.getItemGlow(object)
+function misc:getItemGlow(object)
return br._G.IsQuestObject(object)
end
+
+function misc:AcceptQueues()
+ if misc:getOptionCheck("Accept Queues") then
+ -- Accept Queues
+ -- local randomReady = math.random(8, 15)
+ -- add some randomness
+ if misc.readyToAccept and misc.readyToAccept <= br._G.GetTime() - 5 then
+ br._G.AcceptProposal()
+ misc.readyToAccept = nil
+ -- randomReady = nil
+ end
+ end
+end
\ No newline at end of file
diff --git a/System/Functions/Power.lua b/System/Functions/Power.lua
index d777124886..0b57f5bc88 100644
--- a/System/Functions/Power.lua
+++ b/System/Functions/Power.lua
@@ -1,66 +1,76 @@
local _, br = ...
+br.functions.power = br.functions.power or {}
+local power = br.functions.power
local runeTable = {}
-function br.getChi(Unit)
+
+function power:getChi(Unit)
return br._G.UnitPower(Unit, 12)
end
-function br.getChiMax(Unit)
+function power:getChiMax(Unit)
return br._G.UnitPowerMax(Unit, 12)
end
-- if getCombo() >= 1 then
-function br.getCombo()
- return br._G.UnitPower("player", 4) --GetComboPoints("player") - Legion Change
+function power:getCombo(thisUnit)
+ if thisUnit == nil then thisUnit = "target" end
+ return br._G.GetComboPoints("player", thisUnit) --GetComboPoints("player") - Legion Change
end
-function br.getEmber(Unit)
+function power:getEmber(Unit)
return br._G.UnitPower(Unit, 14)
end
-function br.getEmberMax(Unit)
+function power:getEmberMax(Unit)
return br._G.UnitPowerMax(Unit, 14)
end
-- if getMana("target") <= 15 then
-function br.getMana(Unit)
+function power:getMana(Unit)
return 100 * br._G.UnitPower(Unit, 0) / br._G.UnitPowerMax(Unit, 0)
end
-function br.getEssence(Unit)
+function power:getEssence(Unit)
return br._G.UnitPower(Unit, 19)
end
-function br.getEssenceMax(Unit)
+function power:getEssenceMax(Unit)
return br._G.UnitPowerMax(Unit, 19)
end
--- if br.getPower("target") <= 15 then
-function br.getPower(Unit, index)
- local value = br._G.UnitPower(Unit, index)
- if select(3, br._G.UnitClass("player")) == 11 or select(3, br._G.UnitClass("player")) == 4 then
- -- Druid: Incarnation affects all energy values report as having 20% more
- if br.UnitBuffID("player", 102543) then
- value = value * 1.2
+-- if br.functions.power:getPower("target") <= 15 then
+function power:getPower(Unit, index)
+ local value = index == 4 and br._G.GetComboPoints("player", Unit) or br._G.UnitPower(Unit, index)
+ -- Only apply Druid buffs to energy (index 3)
+ if index == 3 and select(3, br._G.UnitClass("player")) == 11 then
+ -- Druid: Clearcasting affects energy values as if there is max energy available
+ if br.functions.aura:UnitBuffID("player", 16870) then
+ value = br._G.UnitPowerMax(Unit, index)
+ end
+ -- Druid: Berserk affects energy values report as having 50% more
+ if br.functions.aura:UnitBuffID("player", 106951) then
+ value = value * 5
end
end
return value
end
-function br.getPowerMax(Unit, index)
+function power:getPowerMax(Unit, index)
local value = br._G.UnitPowerMax(Unit, index)
- if select(3, br._G.UnitClass("player")) == 11 or select(3, br._G.UnitClass("player")) == 4 then
- -- Druid: Incarnation affects all energy values report as having 20% more
- if br.UnitBuffID("player", 102543) then
- value = value * 1.2
- end
+ -- Only apply Druid buffs to energy (index 3)
+ if index == 3 and select(3, br._G.UnitClass("player")) == 11 then
+ -- Druid: Berserk halves costs -> treat energy max as doubled for checks
+ if br.functions.aura:UnitBuffID("player", 106951) then
+ value = value * 2
+ end
end
return value
end
-function br.getPowerAlt(Unit)
+function power:getPowerAlt(Unit)
local value = 0
local class = select(2, br._G.UnitClass(Unit))
- local spec = br._G.GetSpecialization()
+ local spec = br._G.C_SpecializationInfo.GetSpecialization()
local power = br._G.UnitPower
if (class == "DRUID" and spec == 2) or class == "ROGUE" then
value = power(Unit, 4)
@@ -77,158 +87,162 @@ function br.getPowerAlt(Unit)
return value
end
--- Rune Tracking Table
-function br.getRuneInfo()
- local bCount = 0
- local uCount = 0
- local fCount = 0
- local dCount = 0
- local bPercent = 0
- local uPercent = 0
- local fPercent = 0
- local dPercent = 0
- _G.table.wipe(runeTable)
+-- Return array of 6 rune objects suitable for SimC-style logic
+-- rune object: { base = "blood"|"frost"|"unholy", is_death = bool, ready = bool }
+function power:getRuneObjects()
+ local runes = {}
for i = 1, 6 do
- local CDstart = select(1, br._G.GetRuneCooldown(i))
- local CDduration = select(2, br._G.GetRuneCooldown(i))
- local CDready = select(3, br._G.GetRuneCooldown(i))
- local CDrune = CDduration - (br._G.GetTime() - CDstart)
- local CDpercent = 0
- local runePercent = 0
- local runeCount = 0
- local runeCooldown = 0
- local dCooldown
- local bCooldown
- local uCooldown
- local fCooldown
- if CDrune > CDduration then
- CDpercent = 1 - (CDrune / (CDduration * 2))
- else
- CDpercent = 1 - CDrune / CDduration
+ local rt = br._G.GetRuneType(i)
+ local is_death = (rt == 4)
+ local base
+ -- if i <= 2 then
+ -- base = "blood"
+ -- elseif i <= 4 then
+ -- base = "frost"
+ -- else
+ -- base = "unholy"
+ -- end
+ if rt == 1 then
+ base = "blood"
+ elseif rt == 2 then
+ base = "frost"
+ elseif rt == 3 then
+ base = "unholy"
end
- if not CDready then
- runePercent = CDpercent
- runeCount = 0
- runeCooldown = CDrune
+ local start, duration, ready = br._G.GetRuneCooldown(i)
+ runes[i] = { base = base, is_death = is_death, ready = ready }
+ end
+ return runes
+end
+
+-- SimC-style rune counting
+-- rt: "blood"|"frost"|"unholy"|"death"
+-- include_death: bool - if true, death runes count toward base types
+-- position: optional numeric positional query (1 or 2) for base types, or Nth death rune when rt=="death"
+function power:runesCount(rt, include_death, position)
+ rt = rt and string.lower(rt) or nil
+ local runes = power:getRuneObjects()
+
+ -- positional base-type query (e.g., second blood slot)
+ if position and position > 0 and (rt == "blood" or rt == "frost" or rt == "unholy") then
+ local ti = (rt == "blood") and 1 or (rt == "frost") and 2 or (rt == "unholy") and 3
+ if not ti then return 0 end
+ local idx = (ti - 1) * 2 + position -- Lua 1-based index
+ local r = runes[idx]
+ return (r and r.ready) and 1 or 0
+ end
+
+ local result = 0
+ local rpc = 0
+ for i = 1, 6 do
+ local r = runes[i]
+ -- positional death-query (nth death rune)
+ if position and position ~= 0 and rt == "death" and r.is_death then
+ rpc = rpc + 1
+ if rpc == position then
+ return r.ready and 1 or 0
+ end
+
else
- runePercent = 1
- runeCount = 1
- runeCooldown = 0
- end
- if br._G.GetRuneType(i) == 4 then
- dPercent = runePercent
- dCount = runeCount
- dCooldown = runeCooldown
- runeTable[#runeTable + 1] = {
- Type = "death",
- Index = i,
- Count = dCount,
- Percent = dPercent,
- Cooldown =
- dCooldown
- }
- end
- if br._G.GetRuneType(i) == 1 then
- bPercent = runePercent
- bCount = runeCount
- bCooldown = runeCooldown
- runeTable[#runeTable + 1] = {
- Type = "blood",
- Index = i,
- Count = bCount,
- Percent = bPercent,
- Cooldown =
- bCooldown
- }
- end
- if br._G.GetRuneType(i) == 2 then
- uPercent = runePercent
- uCount = runeCount
- uCooldown = runeCooldown
- runeTable[#runeTable + 1] = {
- Type = "unholy",
- Index = i,
- Count = uCount,
- Percent = uPercent,
- Cooldown =
- uCooldown
- }
- end
- if br._G.GetRuneType(i) == 3 then
- fPercent = runePercent
- fCount = runeCount
- fCooldown = runeCooldown
- runeTable[#runeTable + 1] = {
- Type = "frost",
- Index = i,
- Count = fCount,
- Percent = fPercent,
- Cooldown =
- fCooldown
- }
+ if ((include_death or rt == "death") and r.is_death) or (r.base == rt) then
+ if r.ready then result = result + 1 end
+ end
end
end
- return runeTable
+
+ return result
end
-- Get Count of Specific Rune Time
-function br.getRuneCount(Type)
- Type = string.lower(Type)
- local runeCount = 0
- for i = 1, 6 do
- if runeTable[i].Type == Type then
- runeCount = runeCount + runeTable[i].Count
- end
+function power:getRuneCount(Type)
+ -- Use SimC-style counting: include death runes for base types
+ Type = string.lower(Type or "")
+ if Type == "" then return 0 end
+ -- for death queries, return strict death count
+ if Type == "death" then
+ return power:runesCount("death", false)
+ end
+ -- for base types include death runes as usable
+ if Type == "blood" or Type == "frost" or Type == "unholy" then
+ return power:runesCount(Type, true)
end
- return runeCount
end
-- Get Colldown Percent Remaining of Specific Runes
-function br.getRunePercent(Type)
- Type = string.lower(Type)
- local runePercent = 0
- -- local runeCooldown = 0
+function power:getRunePercent(Type)
+ -- Compute percent using per-rune cooldowns; include death runes for base types
+ Type = string.lower(Type or "")
+ if Type == "" then return 0 end
+
+ local include_death = (Type == "blood" or Type == "frost" or Type == "unholy")
+ local readyCount = 0
+ local maxCooldownPercent = 0
+ local timeNow = br._G.GetTime()
+
for i = 1, 6 do
- if runeTable[i].Type == Type then --and runeTable[i].Cooldown > runeCooldown then
- runePercent = runeTable[i].Percent
- -- runeCooldown = runeTable[i].Cooldown
+ local start, duration, ready = br._G.GetRuneCooldown(i)
+ local rt = br._G.GetRuneType(i)
+ local base = (rt == 1) and "blood" or (rt == 2) and "frost" or (rt == 3) and "unholy"
+ local is_match = false
+ if Type == "death" then
+ is_match = (rt == 4)
+ else
+ is_match = (base == Type) or (include_death and rt == 4)
+ end
+ if is_match then
+ if ready then
+ readyCount = readyCount + 1
+ else
+ if duration and duration > 0 and start then
+ local CDrune = duration - (timeNow - start)
+ local CDpercent
+ if CDrune > duration then
+ CDpercent = 1 - (CDrune / (duration * 2))
+ else
+ CDpercent = 1 - CDrune / duration
+ end
+ if CDpercent > maxCooldownPercent then maxCooldownPercent = CDpercent end
+ end
+ end
end
end
- if br.getRuneCount(Type) == 2 then
- return 2
- elseif br.getRuneCount(Type) == 1 then
- return runePercent + 1
- else
- return runePercent
- end
+
+ if readyCount >= 2 then return 2 end
+ if readyCount == 1 then return 1 + maxCooldownPercent end
+ return maxCooldownPercent
end
-function br.runeCDPercent(runeIndex)
- if br._G.GetRuneCount(runeIndex) == 0 then
- return (br._G.GetTime() - select(1, br._G.GetRuneCooldown(runeIndex))) /
- select(2, br._G.GetRuneCooldown(runeIndex))
+function power:runeCDPercent(runeIndex)
+ local start, duration, ready = br._G.GetRuneCooldown(runeIndex)
+ if start == nil or duration == 0 then
+ return 0
+ end
+ if not ready then
+ return (br._G.GetTime() - start) / duration
else
return 0
end
end
-function br.runeRecharge(runeIndex)
- if not select(3, br._G.GetRuneCooldown(runeIndex)) then
- return select(2, br._G.GetRuneCooldown(runeIndex)) -
- (br._G.GetTime() - select(1, br._G.GetRuneCooldown(runeIndex)))
+function power:runeRecharge(runeIndex)
+ local start, duration, ready = br._G.GetRuneCooldown(runeIndex)
+ if not ready and duration and duration > 0 and start then
+ return duration - (br._G.GetTime() - start)
else
return 0
end
end
-function br.runeTimeTill(runeIndex)
+function power:runeTimeTill(runeIndex)
local runeCDs = {}
local runeCount = 0
local timeTill = 0
for i = 1, 6 do
- runeCount = runeCount + br._G.GetRuneCount(i)
- if runeCDs[runeIndex] == nil then
- runeCDs[i] = br.runeRecharge(i)
+ local start, duration, ready = br._G.GetRuneCooldown(i)
+ if ready then
+ runeCount = runeCount + 1
+ else
+ runeCDs[i] = br.functions.power:runeRecharge(i)
end
end
if runeCount < runeIndex then
@@ -239,28 +253,53 @@ function br.runeTimeTill(runeIndex)
return timeTill
end
+-- Count total ready runes (simple helper)
+function power:getTotalReadyRunes()
+ local total = 0
+ for i = 1, 6 do
+ local _, _, ready = br._G.GetRuneCooldown(i)
+ if ready then total = total + 1 end
+ end
+ return total
+end
+
+-- Return the maximum rune cooldown percent among all runes (0..1)
+function power:getMaxRuneCDPercent()
+ local maxPct = 0
+ for i = 1, 6 do
+ local pct = power:runeCDPercent(i)
+ if pct > maxPct then maxPct = pct end
+ end
+ return maxPct
+end
+
+-- Fractional rune value used by DKs: ready runes + highest CD percent
+function power:getRuneFrac()
+ return power:getTotalReadyRunes() + power:getMaxRuneCDPercent()
+end
+
-- if getTimeToMax("player") < 3 then
-function br.getTimeToMax(Unit, Limit)
+function power:getTimeToMax(Unit, Limit)
local timeTill = 999
local max = Limit or br._G.UnitPowerMax(Unit)
local curr = br._G.UnitPower(Unit)
local curr2 = curr
local _, regen = br._G.GetPowerRegen(Unit)
- if select(3, br._G.UnitClass("player")) == 11 and br._G.GetSpecialization() == 2 and br.isKnown(114107) then
- curr2 = curr + 4 * br.getCombo()
+ if select(3, br._G.UnitClass("player")) == 11 and br._G.C_SpecializationInfo.GetSpecialization() == 2 and br.functions.spell:isKnown(114107) then
+ curr2 = curr + 4 * br.functions.power:getCombo()
end
timeTill = (curr2 > max) and 0 or (max - curr2) * (1.0 / regen)
return timeTill
end
-- /dump getCastRegen(185358)
-function br.getCastRegen(spellId)
+function power:getCastRegen(spellId)
local regenRate = br._G.GetPowerRegen("player")
local power = 0
-- Get the "execute time" of the spell (larger of GCD or the cast time).
- local castTime = br.getCastTime(spellId) or 0
- local gcd = br.getSpellCD(61304)
+ local castTime = br.functions.cast:getCastTime(spellId) or 0
+ local gcd = br.functions.spell:getGlobalCD()
if gcd == 0 then
gcd = br._G.max(1, 1.5 / (1 + br._G.UnitSpellHaste("player") / 100))
end
@@ -270,13 +309,36 @@ function br.getCastRegen(spellId)
return power
end
--- if br.getRegen("player") > 15 then
-function br.getRegen(Unit)
+-- if br.functions.power:getRegen("player") > 15 then
+function power:getRegen(Unit)
return select(2, br._G.GetPowerRegen(Unit))
end
-function br.getSpellCost(spell)
- local t = br._G.C_Spell.GetSpellPowerCost(br._G.GetSpellInfo(spell))
+function power:getSpellCost(spell)
+ if spell == nil then
+ return 0
+ end
+
+ local spellKey = spell
+ if type(spell) == "table" then
+ if type(spell.id) == "function" then
+ local ok, value = pcall(spell.id)
+ if ok and value ~= nil then
+ spellKey = value
+ end
+ elseif type(spell.id) == "number" then
+ spellKey = spell.id
+ end
+ end
+
+ local t = br._G.C_Spell.GetSpellPowerCost(spellKey)
+ if (not t or not t[1]) and type(spellKey) == "number" then
+ -- Fallback for older clients/APIs that may prefer spellName.
+ local spellName = br.api.wow.GetSpellInfo(spellKey)
+ if spellName then
+ t = br._G.C_Spell.GetSpellPowerCost(spellName)
+ end
+ end
if not t then
return 0
elseif not t[1] then
@@ -292,8 +354,8 @@ function br.getSpellCost(spell)
end
end
-function br.hasResources(spell, offset)
- local cost, _, costtype = br.getSpellCost(spell)
+function power:hasResources(spell, offset)
+ local cost, _, costtype = br.functions.power:getSpellCost(spell)
offset = offset or 0
if not cost then
return false
diff --git a/System/Functions/Range.lua b/System/Functions/Range.lua
index 04af4cccc6..207c061f12 100644
--- a/System/Functions/Range.lua
+++ b/System/Functions/Range.lua
@@ -1,4 +1,6 @@
local _, br = ...
+br.functions.range = br.functions.range or {}
+local range = br.functions.range
local sqrt, abs, atan, deg, tan = math.sqrt, math.abs, math.atan, math.deg, math.tan
local testSpell = {
["WARRIOR"] = 6552,
@@ -14,36 +16,38 @@ local testSpell = {
["SHUNTER"] = 186270
}
-function br.getDistance(Unit1, Unit2, option)
+function range:getDistance(Unit1, Unit2, option)
if Unit2 == nil then
Unit2 = Unit1
Unit1 = "player"
end
- -- if br.units ~= nil then
- -- if (Unit1 == "player" and br.units[br._G.ObjectPointer(Unit2)] ~= nil) then
- -- return br.units[br._G.ObjectPointer(Unit2)].range
+ -- if br.engines.enemiesEngine.units ~= nil then
+ -- if (Unit1 == "player" and br.engines.enemiesEngine.units[br._G.ObjectPointer(Unit2)] ~= nil) then
+ -- return br.engines.enemiesEngine.units[br._G.ObjectPointer(Unit2)].range
-- end
- -- if (br.units[br._G.ObjectPointer(Unit1)] ~= nil and Unit2 == "player") then
- -- return br.units[br._G.ObjectPointer(Unit1)].range
+ -- if (br.engines.enemiesEngine.units[br._G.ObjectPointer(Unit1)] ~= nil and Unit2 == "player") then
+ -- return br.engines.enemiesEngine.units[br._G.ObjectPointer(Unit1)].range
-- end
- -- local thisUnit = Unit1 == "player" and br.units[br._G.ObjectPointer(Unit2)] or
- -- br.units[br._G.ObjectPointer(Unit1)]
+ -- local thisUnit = Unit1 == "player" and br.engines.enemiesEngine.units[br._G.ObjectPointer(Unit2)] or
+ -- br.engines.enemiesEngine.units[br._G.ObjectPointer(Unit1)]
-- if thisUnit ~= nil then return thisUnit.range end
-- end
- return br.getDistanceCalc(Unit1, Unit2, option)
+ return range:getDistanceCalc(Unit1, Unit2, option)
end
-function br.getDistanceCalc(Unit1, Unit2, option)
+function range:getDistanceCalc(Unit1, Unit2, option)
local currentDist = 100
local meleeSpell = nil
local playerClass = select(2, br._G.UnitClass("player"))
- local playerSpec = br._G.GetSpecializationInfo(br._G.GetSpecialization())
+ local playerSpec = br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization())
if testSpell[playerClass] ~= nil then
meleeSpell = testSpell[playerClass]
- elseif playerClass == "DRUID" and br.UnitBuffID("player", 768) then
+ elseif playerClass == "DRUID" and br.functions.aura:UnitBuffID("player", 768) then
meleeSpell = testSpell["DRUIDC"]
- elseif playerClass == "DRUID" and br.UnitBuffID("player", 5487) then
+ elseif playerClass == "DRUID" and br.functions.aura:UnitBuffID("player", 5487) then
meleeSpell = testSpell["DRUIDB"]
+ elseif playerClass == "DRUID" then
+ meleeSpell = testSpell["DRUIDC"]
elseif playerSpec == 255 then
meleeSpell = testSpell["SHUNTER"]
elseif playerSpec == 263 then
@@ -59,19 +63,23 @@ function br.getDistanceCalc(Unit1, Unit2, option)
Unit1 = "player"
end
if Unit1 == nil or Unit2 == nil then return 100 end
+ if Unit1 == Unit2 or br._G.UnitIsUnit(Unit1, Unit2) then return 0 end
if option == nil then option = "none" end
-- Check if objects exists and are visible
- if (br.GetUnitIsUnit(Unit1, "player") or (br.GetObjectExists(Unit1) and br.GetUnitIsVisible(Unit1) == true))
- and (br.GetUnitIsUnit(Unit2, "player") or (br.GetObjectExists(Unit2) and br.GetUnitIsVisible(Unit2) == true))
+ if (br.functions.unit:GetUnitIsUnit(Unit1, "player") or (br.functions.unit:GetObjectExists(Unit1) and br.functions.unit:GetUnitIsVisible(Unit1) == true))
+ and (br.functions.unit:GetUnitIsUnit(Unit2, "player") or (br.functions.unit:GetObjectExists(Unit2) and br.functions.unit:GetUnitIsVisible(Unit2) == true))
then
- -- If melee spell is usable, ignore all other calcs
+ -- If melee spell is in range, return 0 (accurate melee range detection)
if meleeSpell ~= nil then
- if Unit2 == "player" then
- if br._G.C_Spell.IsSpellInRange(select(1, br._G.GetSpellInfo(meleeSpell)), Unit1) == 1 then
+ if br._G.UnitIsUnit(Unit2, "player") and not br._G.UnitIsUnit(Unit1, "player") then
+ local spellName = select(1, br.api.wow.GetSpellInfo(meleeSpell))
+ if not br.isClassic and not br.isBC and spellName and br._G.C_Spell.IsSpellInRange(spellName, Unit1) == true then
return 0
end
- else
- if br._G.C_Spell.IsSpellInRange(select(1, br._G.GetSpellInfo(meleeSpell)), Unit2) == 1 then
+ end
+ if br._G.UnitIsUnit(Unit1, "player") and not br._G.UnitIsUnit(Unit2, "player") then
+ local spellName = select(1, br.api.wow.GetSpellInfo(meleeSpell))
+ if not br.isClassic and not br.isBC and spellName and br._G.C_Spell.IsSpellInRange(spellName, Unit2) == true then
return 0
end
end
@@ -82,29 +90,29 @@ function br.getDistanceCalc(Unit1, Unit2, option)
-- if not string.find(Unit1,"0x") then Unit1 = br._G.GetObjectWithGUID(br._G.UnitGUID(Unit1)) end
-- if not string.find(Unit2,"0x") then Unit2 = br._G.GetObjectWithGUID(br._G.UnitGUID(Unit2)) end
--Unit1 Position
- local unit1GUID = select(2, br.getGUID(Unit1))
- if br.unitSetup ~= nil and br.unitSetup.cache[Unit1] ~= nil and br.unitSetup.cache[Unit1].posX ~= nil then
- X1, Y1, Z1 = br.unitSetup.cache[Unit1].posX, br.unitSetup.cache[Unit1].posY, br.unitSetup.cache[Unit1].posZ
- elseif br.GetUnitIsUnit(Unit1, "player") and br.player ~= nil and br.player.posX ~= nil then
+ local unit1GUID = select(2, br.functions.unit:getGUID(Unit1))
+ if br.engines.enemiesEngine ~= nil and br.engines.enemiesEngine.unitSetup.cache[Unit1] ~= nil and br.engines.enemiesEngine.unitSetup.cache[Unit1].posX ~= nil then
+ X1, Y1, Z1 = br.engines.enemiesEngine.unitSetup.cache[Unit1].posX, br.engines.enemiesEngine.unitSetup.cache[Unit1].posY, br.engines.enemiesEngine.unitSetup.cache[Unit1].posZ
+ elseif br.functions.unit:GetUnitIsUnit(Unit1, "player") and br.player ~= nil and br.player.posX ~= nil then
X1, Y1, Z1 = br.player.posX, br.player.posY, br.player.posZ
- elseif br.isChecked("HE Active") and br.memberSetup ~= nil and br.memberSetup.cache[unit1GUID] ~= nil and br.memberSetup.cache[unit1GUID].x ~= nil then
- X1, Y1, Z1 = br.memberSetup.cache[unit1GUID].x, br.memberSetup.cache[unit1GUID].y,
- br.memberSetup.cache[unit1GUID].z
+ elseif br.functions.misc:isChecked("HE Active") and br.engines.healingEngine.memberSetup ~= nil and br.engines.healingEngine.memberSetup.cache[unit1GUID] ~= nil and br.engines.healingEngine.memberSetup.cache[unit1GUID].x ~= nil then
+ X1, Y1, Z1 = br.engines.healingEngine.memberSetup.cache[unit1GUID].x, br.engines.healingEngine.memberSetup.cache[unit1GUID].y,
+ br.engines.healingEngine.memberSetup.cache[unit1GUID].z
else
- X1, Y1, Z1 = br.GetObjectPosition(Unit1)
+ X1, Y1, Z1 = br.functions.unit:GetObjectPosition(Unit1)
end
if not X1 then return 999 end
--Unit2 Position
- local unit2GUID = select(2, br.getGUID(Unit2))
- if br.unitSetup ~= nil and br.unitSetup.cache[Unit2] ~= nil and br.unitSetup.cache[Unit2].posX ~= nil then
- X2, Y2, Z2 = br.unitSetup.cache[Unit2].posX, br.unitSetup.cache[Unit2].posY, br.unitSetup.cache[Unit2].posZ
- elseif br.GetUnitIsUnit(Unit2, "player") and br.player ~= nil and br.player.posX ~= nil then
+ local unit2GUID = select(2, br.functions.unit:getGUID(Unit2))
+ if br.engines.enemiesEngine ~= nil and br.engines.enemiesEngine.unitSetup.cache[Unit2] ~= nil and br.engines.enemiesEngine.unitSetup.cache[Unit2].posX ~= nil then
+ X2, Y2, Z2 = br.engines.enemiesEngine.unitSetup.cache[Unit2].posX, br.engines.enemiesEngine.unitSetup.cache[Unit2].posY, br.engines.enemiesEngine.unitSetup.cache[Unit2].posZ
+ elseif br.functions.unit:GetUnitIsUnit(Unit2, "player") and br.player ~= nil and br.player.posX ~= nil then
X2, Y2, Z2 = br.player.posX, br.player.posY, br.player.posZ
- elseif br.isChecked("HE Active") and br.memberSetup ~= nil and br.memberSetup.cache[unit2GUID] ~= nil and br.memberSetup.cache[unit2GUID].x ~= nil then
- X2, Y2, Z2 = br.memberSetup.cache[unit2GUID].x, br.memberSetup.cache[unit2GUID].y,
- br.memberSetup.cache[unit2GUID].z
+ elseif br.functions.misc:isChecked("HE Active") and br.engines.healingEngine.memberSetup ~= nil and br.engines.healingEngine.memberSetup.cache[unit2GUID] ~= nil and br.engines.healingEngine.memberSetup.cache[unit2GUID].x ~= nil then
+ X2, Y2, Z2 = br.engines.healingEngine.memberSetup.cache[unit2GUID].x, br.engines.healingEngine.memberSetup.cache[unit2GUID].y,
+ br.engines.healingEngine.memberSetup.cache[unit2GUID].z
else
- X2, Y2, Z2 = br.GetObjectPosition(Unit2)
+ X2, Y2, Z2 = br.functions.unit:GetObjectPosition(Unit2)
end
if not X2 then return 999 end
-- Melee Range Bypass
@@ -121,11 +129,21 @@ function br.getDistanceCalc(Unit1, Unit2, option)
local PlayerCombatReach = br._G.UnitCombatReach(Unit1) or 0
local MeleeCombatReachConstant = 7 / 3
local IfSourceAndTargetAreRunning = 0
- if br.isMoving(Unit1) and br.isMoving(Unit2) then IfSourceAndTargetAreRunning = 8 / 3 end
+ if br.functions.misc:isMoving(Unit1) and br.functions.misc:isMoving(Unit2) then IfSourceAndTargetAreRunning = 8 / 3 end
+
+ -- Classic fallback: determine melee range from positions alone.
+ -- Uses the same constants as the meleeRange calculation below.
+ if (br.isClassic or br.isBC) and meleeSpell ~= nil and (br._G.UnitIsUnit(Unit1, "player") or br._G.UnitIsUnit(Unit2, "player")) then
+ local edgeToEdge = sqrt(((X2 - X1) ^ 2) + ((Y2 - Y1) ^ 2) + ((Z2 - Z1) ^ 2)) - (PlayerCombatReach + TargetCombatReach)
+ local meleeEdgeDistance = MeleeCombatReachConstant + IfSourceAndTargetAreRunning + rangeMod
+ if edgeToEdge <= meleeEdgeDistance then
+ return 0
+ end
+ end
-- Rogue Melee Range Increase Mod
if br.player ~= nil then
if br.player.talent.acrobaticStrikes ~= nil and meleeSpell ~= nil then
- if br.player.talent.acrobaticStrikes and option ~= "noMod" and br._G.C_Spell.IsSpellInRange(select(1, br._G.GetSpellInfo(meleeSpell)), Unit2) == 1 then
+ if br.player.talent.acrobaticStrikes and option ~= "noMod" and br._G.C_Spell.IsSpellInRange(select(1, br.api.wow.GetSpellInfo(meleeSpell)), Unit2) == true then
rangeMod = 3
end
end
@@ -133,7 +151,7 @@ function br.getDistanceCalc(Unit1, Unit2, option)
local dist = sqrt(((X2 - X1) ^ 2) + ((Y2 - Y1) ^ 2) + ((Z2 - Z1) ^ 2)) - (PlayerCombatReach + TargetCombatReach) -
rangeMod
local dist2 = dist + 0.03 * ((13 - dist) / 0.13)
- local dist3 = dist + 0.05 * ((8 - dist) / 0.15) + 1
+ local dist3 = dist + 0.05 * ((8 - dist) / 0.15) --+ 1
local dist4 = dist + (PlayerCombatReach + TargetCombatReach)
local meleeRange = br._G.max(6,
PlayerCombatReach + TargetCombatReach + MeleeCombatReachConstant + IfSourceAndTargetAreRunning)
@@ -142,10 +160,6 @@ function br.getDistanceCalc(Unit1, Unit2, option)
if option == "dist3" then return dist3 end
if option == "dist4" then return dist4 end
if option == "meleeRange" then return meleeRange end
- -- if (br._G.UnitIsUnit("target", Unit1) or br._G.UnitIsUnit("target", Unit2)) then
- -- br._G.print("Dist: " ..
- -- tostring(br.round2(PlayerCombatReach, 0) .. ", " .. tostring(br.round2(TargetCombatReach, 0))))
- -- end
-- currentDist = br._G.max(dist, meleeRange, 0)
if dist > 13 then
currentDist = br._G.max(0, dist)
@@ -156,39 +170,39 @@ function br.getDistanceCalc(Unit1, Unit2, option)
elseif dist4 > meleeRange then -- Thanks Ssateneth
currentDist = dist4
else
- currentDist = 0
+ currentDist = 5
end
-- Modifier for Mastery: Sniper Training (Hunter - Marksmanship)
- -- if currentDist < 100 and br.isKnown(193468) and option ~= "noMod" and (Unit1 == "player" or Unit2 == "player") then
+ -- if currentDist < 100 and br.functions.spell:isKnown(193468) and option ~= "noMod" and (Unit1 == "player" or Unit2 == "player") then
-- currentDist = currentDist - (currentDist * (GetMasteryEffect() / 100))
-- end
end
return currentDist
end
-function br.isInRange(spellID, unit)
+function range:isInRange(spellID, unit)
return br._G.LibStub("SpellRange-1.0").C_Spell.IsSpellInRange(spellID, unit)
end
-function br.getDistanceToLocation(Unit1, X2, Y2, Z2)
+function range:getDistanceToLocation(Unit1, X2, Y2, Z2)
if Unit1 == nil then
Unit1 = "player"
end
- if br.GetObjectExists(Unit1) and br.GetUnitIsVisible(Unit1) then
- local X1, Y1, Z1 = br.GetObjectPosition(Unit1)
+ if br.functions.unit:GetObjectExists(Unit1) and br.functions.unit:GetUnitIsVisible(Unit1) then
+ local X1, Y1, Z1 = br.functions.unit:GetObjectPosition(Unit1)
return sqrt(((X2 - X1) ^ 2) + ((Y2 - Y1) ^ 2) + ((Z2 - Z1) ^ 2))
else
return 100
end
end
-function br.getFacingDistance()
- if br.GetUnitIsVisible("player") and br.GetUnitIsVisible("target") then
+function range:getFacingDistance()
+ if br.functions.unit:GetUnitIsVisible("player") and br.functions.unit:GetUnitIsVisible("target") then
--local targetDistance = getRealDistance("target")
- local targetDistance = br.getDistance("target")
- local Y1, X1 = br.GetObjectPosition("player");
- local Y2, X2 = br.GetObjectPosition("target");
- local angle1 = br.GetObjectFacing("player")
+ local targetDistance = br.functions.range:getDistance("target")
+ local Y1, X1 = br.functions.unit:GetObjectPosition("player");
+ local Y2, X2 = br.functions.unit:GetObjectPosition("target");
+ local angle1 = br.functions.unit:GetObjectFacing("player")
local angle2
local deltaY = Y2 - Y1
local deltaX = X2 - X1
@@ -198,7 +212,7 @@ function br.getFacingDistance()
elseif deltaX < 0 then
angle2 = deg(atan(deltaY / deltaX) + (math.pi / 2))
end
- local Dist = br.round2(math.tan(abs(angle2 - angle1) * math.pi / 180) * targetDistance * 10000) / 10000
+ local Dist = br.functions.misc:round2(math.tan(abs(angle2 - angle1) * math.pi / 180) * targetDistance * 10000) / 10000
if br._G.ObjectIsFacing("player", "target") then
return Dist
else
@@ -210,20 +224,20 @@ function br.getFacingDistance()
end
-- /dump getTotemDistance("target")
-function br.getTotemDistance(Unit1)
+function range:getTotemDistance(Unit1)
if Unit1 == nil then
Unit1 = "player"
end
- if br.GetUnitIsVisible(Unit1) then
+ if br.functions.unit:GetUnitIsVisible(Unit1) then
-- local objectCount = GetObjectCountBR() or 0
local X2, Y2, Z2
for i = 1, br._G.GetObjectCount() do
- if br.GetUnitIsUnit(br._G.UnitCreator(br._G.GetObjectWithIndex(i)), "Player") and (br._G.UnitName(br._G.GetObjectWithIndex(i)) == "Searing Totem" or br._G.UnitName(br._G.GetObjectWithIndex(i)) == "Magma Totem") then
- X2, Y2, Z2 = br.GetObjectPosition(br._G.GetObjectWithIndex(i))
+ if br.functions.unit:GetUnitIsUnit(br._G.UnitCreator(br._G.GetObjectWithIndex(i)), "Player") and (br._G.UnitName(br._G.GetObjectWithIndex(i)) == "Searing Totem" or br._G.UnitName(br._G.GetObjectWithIndex(i)) == "Magma Totem") then
+ X2, Y2, Z2 = br.functions.unit:GetObjectPosition(br._G.GetObjectWithIndex(i))
end
end
- local X1, Y1, Z1 = br.GetObjectPosition(Unit1)
+ local X1, Y1, Z1 = br.functions.unit:GetObjectPosition(Unit1)
local totemDistance = sqrt(((X2 - X1) ^ 2) + ((Y2 - Y1) ^ 2) + ((Z2 - Z1) ^ 2))
--Print(TotemDistance)
return totemDistance
@@ -233,21 +247,21 @@ function br.getTotemDistance(Unit1)
end
-- if isInMelee() then
-function br.isInMelee(Unit)
+function range:isInMelee(Unit)
if Unit == nil then
Unit = "target"
end
- if br.getDistance(Unit) < 4 then
+ if br.functions.range:getDistance(Unit) < 4 then
return true
else
return false
end
end
-function br.isSafeToAoE(spellID, Unit, effectRng, minUnits, aoeType, enemies)
- if not br.isChecked("Safe Damage Check") then return true end
+function range:isSafeToAoE(spellID, Unit, effectRng, minUnits, aoeType, enemies)
+ if not br.functions.misc:isChecked("Safe Damage Check") then return true end
local enemiesValid, enemiesAll
- local maxRange = select(6, br._G.GetSpellInfo(spellID))
+ local maxRange = select(6, br.api.wow.GetSpellInfo(spellID))
if effectRng == nil then effectRng = 5 end
if maxRange == nil or maxRange == 0 then
maxRange = tonumber(effectRng)
@@ -257,30 +271,30 @@ function br.isSafeToAoE(spellID, Unit, effectRng, minUnits, aoeType, enemies)
end
if minUnits == nil then minUnits = 1 end
if aoeType == "rect" then
- enemiesValid = br.getEnemiesInRect(effectRng, maxRange, false)
- enemiesAll = not enemies and br.getEnemiesInRect(effectRng, maxRange, false, true) or enemies
+ enemiesValid = br.engines.enemiesEngineFunctions:getEnemiesInRect(effectRng, maxRange, false)
+ enemiesAll = not enemies and br.engines.enemiesEngineFunctions:getEnemiesInRect(effectRng, maxRange, false, true) or enemies
elseif aoeType == "cone" then
- enemiesValid = br.getEnemiesInCone(180, effectRng, false)
- enemiesAll = not enemies and br.getEnemiesInCone(180, effectRng, false, true) or enemies
+ enemiesValid = br.engines.enemiesEngineFunctions:getEnemiesInCone(180, effectRng, false)
+ enemiesAll = not enemies and br.engines.enemiesEngineFunctions:getEnemiesInCone(180, effectRng, false, true) or enemies
else
- enemiesValid = #br.getEnemies(Unit, effectRng)
- enemiesAll = not enemies and #br.getEnemies(Unit, effectRng, true) or enemies
+ enemiesValid = #br.engines.enemiesEngineFunctions:getEnemies(Unit, effectRng)
+ enemiesAll = not enemies and #br.engines.enemiesEngineFunctions:getEnemies(Unit, effectRng, true) or enemies
end
return enemiesValid >= minUnits and enemiesValid >= enemiesAll
end
-function br.inRange(spellID, unit)
- local spellName = br._G.GetSpellInfo(spellID)
+function range:inRange(spellID, unit)
+ local spellName = br.api.wow.GetSpellInfo(spellID)
if unit == nil then unit = "target" end
local inRange = br._G.C_Spell.IsSpellInRange(spellName, unit)
if inRange ~= nil then
- return br._G.C_Spell.IsSpellInRange(spellName, unit) == 1
+ return br._G.C_Spell.IsSpellInRange(spellName, unit) == true
else
return false
end
end
-function br.getBaseDistance(unit1, unit2)
+function range:getBaseDistance(unit1, unit2)
if unit2 == nil then
unit2 = "player"
end
diff --git a/System/Functions/SmartQueue.lua b/System/Functions/SmartQueue.lua
index 2b8bfb4a1a..a89ce8dd4b 100644
--- a/System/Functions/SmartQueue.lua
+++ b/System/Functions/SmartQueue.lua
@@ -1,5 +1,7 @@
local _, br = ...
-br.queueSpell = false
+br.functions.SmartQueue = br.functions.SmartQueue or {}
+local SmartQueue = br.functions.SmartQueue
+SmartQueue.queueSpell = false
local queueSpellTime
local queueSpellPos = { x = 0, y = 0, z = 0 }
local queueSpellTarget
@@ -7,7 +9,7 @@ local smartQueueFrame
local ignoreKeys = { ["LALT"] = true, ["LSHIFT"] = true, ["LCTRL"] = true }
local function checkKeys(self, key)
- if br.player ~= nil and br.player.spells ~= nil and br.player.spells.bindings ~= nil and ignoreKeys[key] == nil and br.isChecked("Smart Queue") and br._G.UnitAffectingCombat("player") then
+ if br.player ~= nil and br.player.spells ~= nil and br.player.spells.bindings ~= nil and ignoreKeys[key] == nil and br.functions.misc:isChecked("Smart Queue") and br._G.UnitAffectingCombat("player") then
local pressedKey = ""
if br._G.IsLeftShiftKeyDown() then
pressedKey = "SHIFT-"
@@ -19,19 +21,19 @@ local function checkKeys(self, key)
pressedKey = pressedKey .. key
local spell = br.player.spells.bindings[pressedKey]
if spell ~= nil then
- local cd = br.getSpellCD(spell)
- if br._G.GetSpellInfo(br._G.GetSpellInfo(spell)) and cd <= br.getOptionValue("Smart Queue") and br.isChecked(br._G.GetSpellInfo(spell) .. " (Queue)") and (cd > 0 or br._G.C_Spell.IsSpellUsable(spell) == false or br._G.UnitCastingInfo("player")) then
- br.queueSpell = spell
+ local cd = br.functions.spell:getSpellCD(spell)
+ if br.api.wow.GetSpellInfo(br.api.wow.GetSpellInfo(spell)) and cd <= br.functions.misc:getOptionValue("Smart Queue") and br.functions.misc:isChecked(br.api.wow.GetSpellInfo(spell) .. " (Queue)") and (cd > 0 or br._G.C_Spell.IsSpellUsable(spell) == false or br._G.UnitCastingInfo("player")) then
+ SmartQueue.queueSpell = spell
queueSpellTime = br._G.GetTime()
- if br.getOptionValue(br._G.GetSpellInfo(br.queueSpell) .. " (Queue)") == 2 then
+ if br.functions.misc:getOptionValue(br.api.wow.GetSpellInfo(SmartQueue.queueSpell) .. " (Queue)") == 2 then
local x, y = br._G.GetMousePosition()
queueSpellPos.x, queueSpellPos.y, queueSpellPos.z = br._G.ScreenToWorld(x, y)
- elseif br.getOptionValue(br._G.GetSpellInfo(br.queueSpell) .. " (Queue)") == 4 and br._G.UnitIsVisible("mouseover") then
+ elseif br.functions.misc:getOptionValue(br.api.wow.GetSpellInfo(SmartQueue.queueSpell) .. " (Queue)") == 4 and br._G.UnitIsVisible("mouseover") then
queueSpellTarget = br._G.ObjectPointer("mouseover")
else
queueSpellTarget = "target"
end
- br.ChatOverlay("Queued: " .. br._G.GetSpellInfo(br.queueSpell))
+ br.ui.chatOverlay:Show("Queued: " .. br.api.wow.GetSpellInfo(SmartQueue.queueSpell))
end
end
end
@@ -98,15 +100,15 @@ local function spellSuccess(self, event, ...)
local sourceUnit = select(1, ...)
local spellID = select(3, ...)
if sourceUnit == "player" then
- if spellID == br.queueSpell then
- br.ChatOverlay("Casted Queued Spell: " .. br._G.GetSpellInfo(spellID))
- br.queueSpell = false
+ if spellID == SmartQueue.queueSpell then
+ br.ui.chatOverlay:Show("Casted Queued Spell: " .. br.api.wow.GetSpellInfo(spellID))
+ SmartQueue.queueSpell = false
end
end
end
end
-function br.smartQueue()
+function SmartQueue:smartQueue()
if smartQueueFrame == nil then
smartQueueFrame = br._G.CreateFrame("Frame")
smartQueueFrame:SetPropagateKeyboardInput(true)
@@ -117,7 +119,7 @@ function br.smartQueue()
GetKeyBindings()
-- Fix capturing mousebuttons
- if br.isChecked("Smart Queue") then
+ if br.functions.misc:isChecked("Smart Queue") then
if br._G.GetKeyState(0x05) then
checkKeys(nil, "BUTTON4")
elseif br._G.GetKeyState(0x06) then
@@ -125,42 +127,42 @@ function br.smartQueue()
end
end
- if br.queueSpell and (br._G.GetTime() - queueSpellTime) > br.getOptionValue("Smart Queue") then
- br.queueSpell = false
+ if SmartQueue.queueSpell and (br._G.GetTime() - queueSpellTime) > br.functions.misc:getOptionValue("Smart Queue") then
+ SmartQueue.queueSpell = false
end
- if br.queueSpell and br.isChecked("Smart Queue") and (((br._G.GetTime() - queueSpellTime) <= br.getOptionValue("Smart Queue") and not br._G.UnitChannelInfo("player")
- and (br._G.UnitIsVisible(queueSpellTarget) or br.getOptionValue(br._G.GetSpellInfo(br.queueSpell) .. " (Queue)") == 2) and br._G.UnitAffectingCombat("player"))
- or (br._G.IsAoEPending() and br.isChecked("Smart Queue") and br.isChecked(br._G.GetSpellInfo(br.queueSpell) .. " (Queue)") and br._G.UnitAffectingCombat("player")))
- and (br.queueSpell ~= 1776 or br.getFacing("target", "player"))
+ if SmartQueue.queueSpell and br.functions.misc:isChecked("Smart Queue") and (((br._G.GetTime() - queueSpellTime) <= br.functions.misc:getOptionValue("Smart Queue") and not br._G.UnitChannelInfo("player")
+ and (br._G.UnitIsVisible(queueSpellTarget) or br.functions.misc:getOptionValue(br.api.wow.GetSpellInfo(SmartQueue.queueSpell) .. " (Queue)") == 2) and br._G.UnitAffectingCombat("player"))
+ or (br._G.IsAoEPending() and br.functions.misc:isChecked("Smart Queue") and br.functions.misc:isChecked(br.api.wow.GetSpellInfo(SmartQueue.queueSpell) .. " (Queue)") and br._G.UnitAffectingCombat("player")))
+ and (SmartQueue.queueSpell ~= 1776 or br.functions.unit:getFacing("target", "player"))
then
- if br._G.IsAoEPending() and br.getOptionValue(br._G.GetSpellInfo(br.queueSpell) .. " (Queue)") ~= 3 then
- local pendingSpell = br.queueSpell
+ if br._G.IsAoEPending() and br.functions.misc:getOptionValue(br.api.wow.GetSpellInfo(SmartQueue.queueSpell) .. " (Queue)") ~= 3 then
+ local pendingSpell = SmartQueue.queueSpell
br._G.CancelPendingSpell()
- br._G.CastSpellByName(br._G.GetSpellInfo(pendingSpell), "cursor")
+ br._G.CastSpellByName(br.api.wow.GetSpellInfo(pendingSpell), "cursor")
return true
end
- if br.queueSpell and not br._G.UnitCastingInfo("player") then
- if br.getOptionValue(br._G.GetSpellInfo(br.queueSpell) .. " (Queue)") == 2 then
- if br.createCastFunction("player", nil, nil, nil, br.queueSpell, nil, nil, nil, nil, true) then
- if br.castAtPosition(queueSpellPos.x, queueSpellPos.y, queueSpellPos.z, br.queueSpell) then
- br.ChatOverlay("Casted Queued Spell: " .. br._G.GetSpellInfo(br.queueSpell))
+ if SmartQueue.queueSpell and not br._G.UnitCastingInfo("player") then
+ if br.functions.misc:getOptionValue(br.api.wow.GetSpellInfo(SmartQueue.queueSpell) .. " (Queue)") == 2 then
+ if br.functions.cast:createCastFunction("player", nil, nil, nil, SmartQueue.queueSpell, nil, nil, nil, nil, true) then
+ if br.functions.cast:castAtPosition(queueSpellPos.x, queueSpellPos.y, queueSpellPos.z, SmartQueue.queueSpell) then
+ br.ui.chatOverlay:Show("Casted Queued Spell: " .. br.api.wow.GetSpellInfo(SmartQueue.queueSpell))
return true
end
end
- elseif br.getOptionValue(br._G.GetSpellInfo(br.queueSpell) .. " (Queue)") == 3 then
- if br.createCastFunction("player", nil, nil, nil, br.queueSpell, nil, nil, nil, nil, true) then
- br._G.CastSpellByName(br._G.GetSpellInfo(br.queueSpell))
- br.ChatOverlay("Casted Queued Spell: " .. br._G.GetSpellInfo(br.queueSpell))
+ elseif br.functions.misc:getOptionValue(br.api.wow.GetSpellInfo(SmartQueue.queueSpell) .. " (Queue)") == 3 then
+ if br.functions.cast:createCastFunction("player", nil, nil, nil, SmartQueue.queueSpell, nil, nil, nil, nil, true) then
+ br._G.CastSpellByName(br.api.wow.GetSpellInfo(SmartQueue.queueSpell))
+ br.ui.chatOverlay:Show("Casted Queued Spell: " .. br.api.wow.GetSpellInfo(SmartQueue.queueSpell))
return true
end
else
- if br._G.C_Spell.IsSpellInRange(br._G.GetSpellInfo(br.queueSpell), queueSpellTarget) ~= nil then
- if br.createCastFunction(queueSpellTarget, nil, nil, nil, br.queueSpell) then
+ if br._G.C_Spell.IsSpellInRange(br.api.wow.GetSpellInfo(SmartQueue.queueSpell), queueSpellTarget) ~= nil then
+ if br.functions.cast:createCastFunction(queueSpellTarget, nil, nil, nil, SmartQueue.queueSpell) then
return true
end
else
- if br.createCastFunction("player", nil, nil, nil, br.queueSpell) then
+ if br.functions.cast:createCastFunction("player", nil, nil, nil, SmartQueue.queueSpell) then
return true
end
end
diff --git a/System/Functions/Spell.lua b/System/Functions/Spell.lua
index 6b9a538f2b..8bab45133e 100644
--- a/System/Functions/Spell.lua
+++ b/System/Functions/Spell.lua
@@ -1,14 +1,17 @@
local _, br = ...
-function br.castInterrupt(SpellID, Percent, Unit)
+br.functions.spell = br.functions.spell or {}
+local spell = br.functions.spell
+
+function spell:castInterrupt(SpellID, Percent, Unit)
Percent = br._G.Math.min(Percent + math.random(-6, 6), 99)
if Unit == nil then Unit = "target" end
- if br.GetObjectExists(Unit) then
+ if br.functions.unit:GetObjectExists(Unit) then
local _, _, _, castStartTime, castEndTime, _, _, castInterruptable = br._G.UnitCastingInfo(Unit)
local channelName, _, _, channelStartTime, channelEndTime, _, channelInterruptable = br._G.UnitChannelInfo(Unit)
-- first make sure we will be able to cast the spellID
- if br.canCast(SpellID, false, false, Unit) == true then
+ if br.functions.cast:canCast(SpellID, false, false, Unit) == true then
-- make sure we cover melee range
- local allowedDistance = select(6, br._G.GetSpellInfo(SpellID))
+ local allowedDistance = select(6, br.api.wow.GetSpellInfo(SpellID))
if allowedDistance < 5 then
allowedDistance = 5
end
@@ -43,8 +46,8 @@ function br.castInterrupt(SpellID, Percent, Unit)
return false
end
--cast the spell
- if br.getDistance("player", Unit) < allowedDistance then
- if br.castSpell(Unit, SpellID, false, false) then
+ if br.functions.range:getDistance("player", Unit) < allowedDistance then
+ if br.functions.cast:castSpell(Unit, SpellID, false, false) then
return true
end
end
@@ -54,17 +57,18 @@ function br.castInterrupt(SpellID, Percent, Unit)
return false
end
--- br.canInterrupt("target",20)
-function br.canInterrupt(unit, percentint)
+-- br.functions.spell:canInterrupt("target",20)
+function spell:canInterrupt(unit, percentint)
unit = unit or "target"
-- M+ Affix: Beguiling (Prevents Interrupt) - Queen's Decree: Unstoppable buff
- if br.UnitBuffID(unit, 302417) ~= nil then return false end
- local interruptTarget = br.getOptionValue("Interrupt Target")
- if interruptTarget == 2 and not br.GetUnitIsUnit(unit, "target") then
+ local queensDecreeID = br.functions.spell:getHighestSpellID("queensDecreeUnstoppable") or 302417
+ if br.functions.aura:UnitBuffID(unit, queensDecreeID) ~= nil then return false end
+ local interruptTarget = br.functions.misc:getOptionValue("Interrupt Target")
+ if interruptTarget == 2 and not br.functions.unit:GetUnitIsUnit(unit, "target") then
return false
- elseif interruptTarget == 3 and not br.GetUnitIsUnit(unit, "focus") then
+ elseif interruptTarget == 3 and not br.functions.unit:GetUnitIsUnit(unit, "focus") then
return false
- elseif interruptTarget == 4 and br.getOptionValue("Interrupt Mark") ~= br._G.GetRaidTargetIndex(unit) then
+ elseif interruptTarget == 4 and br.functions.misc:getOptionValue("Interrupt Mark") ~= br._G.GetRaidTargetIndex(unit) then
return false
end
local castStartTime, castEndTime, interruptID, interruptable = 0, 0, 0, false
@@ -72,8 +76,8 @@ function br.canInterrupt(unit, percentint)
local channelDelay = 1 -- Delay to mimick human reaction time for channeled spells
local castType = "spellcast" -- Handle difference in logic if the spell is cast or being channeles
local onWhitelist = false
- local whitelistOnly = br.isChecked("Interrupt Only Whitelist")
- if br.GetUnitExists(unit)
+ local whitelistOnly = br.functions.misc:isChecked("Interrupt Only Whitelist")
+ if br.functions.unit:GetUnitExists(unit)
and br._G.UnitCanAttack("player", unit)
and not br._G.UnitIsDeadOrGhost(unit)
then
@@ -140,9 +144,10 @@ function br.canInterrupt(unit, percentint)
if ((whitelistOnly and onWhitelist) or not whitelistOnly)
or not (br.player.instance == "party" or br.player.instance == "raid" or br.player.instance == "scenario")
then
- local ttd = br.getTTD(unit) or 0
+ local ttd = br.engines.ttdTable:getTTD(unit)
local withinsCastPercent = math.ceil((castTimeRemain / castDuration) * 100) <= castPercent
- local willFinishCast = ttd > castTimeRemain or ttd < 0
+ -- ttdTable returns negative values when unknown/unreliable; don't block interrupts in that case.
+ local willFinishCast = (ttd == nil) or (ttd < 0) or (ttd > castTimeRemain)
if castType == "spellcast" then
if withinsCastPercent and interruptable == true and willFinishCast then
return true
@@ -162,17 +167,17 @@ function br.canInterrupt(unit, percentint)
end
end
-function br.canStun(unit)
- return br.isCrowdControlCandidates(unit)
+function spell:canStun(unit)
+ return br.engines.enemiesEngineFunctions:isCrowdControlCandidates(unit)
end
--- if br.getCharges(115399) > 0 then
-function br.getCharges(spellID)
+-- if br.functions.spell:getCharges(115399) > 0 then
+function spell:getCharges(spellID)
local chargeInfo = br._G.C_Spell.GetSpellCharges(spellID)
- return chargeInfo.currentCharges
+ return chargeInfo and chargeInfo.currentCharges or 0
end
-function br.getChargesFrac(spellID, chargeMax)
+function spell:getChargesFrac(spellID, chargeMax)
local chargeInfo = br._G.C_Spell.GetSpellCharges(spellID)
local charges, maxCharges, start, duration = chargeInfo.currentCharges, chargeInfo.maxCharges,
chargeInfo.cooldownStartTime, chargeInfo.cooldownDuration
@@ -193,7 +198,7 @@ function br.getChargesFrac(spellID, chargeMax)
return 0
end
-function br.getRecharge(spellID, chargeMax)
+function spell:getRecharge(spellID, chargeMax)
local chargeInfo = br._G.C_Spell.GetSpellCharges(spellID)
local charges, maxCharges, chargeStart, chargeDuration = chargeInfo.currentCharges, chargeInfo.maxCharges,
chargeInfo.cooldownStartTime, chargeInfo.cooldownDuration
@@ -208,7 +213,7 @@ function br.getRecharge(spellID, chargeMax)
end
-- Full RechargeTime of a Spell/dump getFullRechargeTime(214579)
-function br.getFullRechargeTime(spellID)
+function spell:getFullRechargeTime(spellID)
local chargeInfo = br._G.C_Spell.GetSpellCharges(spellID)
local charges, maxCharges, chargeStart, chargeDuration = chargeInfo.currentCharges, chargeInfo.maxCharges,
chargeInfo.cooldownStartTime, chargeInfo.cooldownDuration
@@ -224,7 +229,7 @@ function br.getFullRechargeTime(spellID)
end
local maxStage = 0
-function br.getEmpoweredRank(spellID)
+function spell:getEmpoweredRank(spellID)
local stage = 0
if br.empowerID and spellID == br.empowerID then
local _, _, _, startTime, _, _, _, _, _, totalStages = br._G.UnitChannelInfo("player")
@@ -248,22 +253,22 @@ function br.getEmpoweredRank(spellID)
return stage
end
--- if br.getSpellCD(12345) <= 0.4 then
-function br.getSpellCD(SpellID)
+-- if br.functions.spell:getSpellCD(12345) <= 0.4 then
+function spell:getSpellCD(SpellID)
if SpellID == nil then return false end
local spellCD = br._G.C_Spell.GetSpellCooldown(SpellID)
if spellCD == nil or spellCD.startTime == 0 then
return 0
else
local MyCD = spellCD.startTime + spellCD.duration - br._G.GetTime()
- MyCD = MyCD or 0 -- getLatency()
+ MyCD = MyCD or 0 -- br.functions.misc:getLatency()
if MyCD < 0 then MyCD = 0 end
return MyCD
end
end
-function br.getGlobalCD(max)
- local currentSpecName = select(2, br._G.GetSpecializationInfo(br._G.GetSpecialization()))
+function spell:getGlobalCD(max)
+ local currentSpecName = select(2, br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization()))
if currentSpecName == "" then currentSpecName = "Initial" end
if max == true then
if currentSpecName == "Feral" or currentSpecName == "Brewmaster" or currentSpecName == "Windwalker" or br._G.UnitClass("player") == "Rogue" then
@@ -272,10 +277,15 @@ function br.getGlobalCD(max)
return math.max(math.max(1, 1.5 / (1 + br._G.UnitSpellHaste("player") / 100)), 0.75)
end
end
- return br.getSpellCD(61304)
+ -- Use Classic-compatible GCD spell if available
+ local gcdSpellID = br.functions.spell:getHighestSpellID("global") or 61304
+ if (br.isClassic or br.isBC) and br.api.wow.GetGCDSpellID then
+ gcdSpellID = br.api.wow.GetGCDSpellID()
+ end
+ return br.functions.spell:getSpellCD(gcdSpellID)
end
-function br.getSpellType(spellName)
+function spell:getSpellType(spellName)
if spellName == nil then return "Invalid" end
local helpful = br._G.C_Spell.IsSpellHelpful(spellName) or false
local harmful = br._G.C_Spell.IsSpellHarmful(spellName) or false
@@ -285,21 +295,22 @@ function br.getSpellType(spellName)
if not helpful and not harmful then return "Unknown" end
end
-function br.getCastingRegen(spellID)
- local regenRate = br.getRegen("player")
+function spell:getCastingRegen(spellID)
+ local regenRate = br.functions.power:getRegen("player")
local power = 0
local desc = br._G.C_Spell.GetSpellDescription(spellID)
local generates = desc:gsub("%D+", "")
local tooltip = tonumber(generates:sub(-2)) or 0
-- Get the "execute time" of the spell (larger of GCD or the cast time).
- local castTime = br.getCastTime(spellID) or 0
+ local castTime = br.functions.cast:getCastTime(spellID) or 0
local gcd = br.player.gcdMax
local castSeconds = (castTime > gcd) and castTime or gcd
power = power + regenRate * castSeconds + tooltip
-- Get the amount of time remaining on the Steady Focus buff.
- if br.UnitBuffID("player", 193534) ~= nil then
- local seconds = br.getBuffRemain("player", 193534)
+ local steadyFocusID = br.functions.spell:getHighestSpellID("steadyFocus") or 193534
+ if br.functions.aura:UnitBuffID("player", steadyFocusID) ~= nil then
+ local seconds = br.functions.aura:getBuffRemain("player", steadyFocusID)
if seconds <= 0 then
seconds = 0
elseif seconds > castSeconds then
@@ -311,8 +322,8 @@ function br.getCastingRegen(spellID)
return power
end
-function br.getSpellRange(spellID, option)
- local _, _, _, _, _, maxRange = br._G.GetSpellInfo(spellID)
+function spell:getSpellRange(spellID, option)
+ local _, _, _, _, _, maxRange = br.api.wow.GetSpellInfo(spellID)
if maxRange == nil or maxRange == 0 then maxRange = 5 else maxRange = tonumber(maxRange) end
-- Modifiers
local rangeMod = 0
@@ -327,25 +338,49 @@ function br.getSpellRange(spellID, option)
return maxRange + rangeMod
end
-function br.isSpellInSpellbook(spellID, spellType)
+function spell:isSpellInSpellbook(spellID, spellType)
local spellSlot = br._G.FindSpellBookSlotBySpellID(spellID, spellType == "pet" and true or false)
if spellSlot then
- local spellName = br._G.C_SpellBook.GetSpellBookItemName(spellSlot, spellType)
- local link = br._G.C_Spell.GetSpellLink(spellName)
- local currentSpellId = tonumber(link and link:gsub("|", "||"):match("spell:(%d+)"))
- return currentSpellId == spellID
+ -- Try modern API first if available
+ if br._G.C_SpellBook and br._G.C_SpellBook.GetSpellBookItemInfo then
+ local spellBookItemInfo = br._G.C_SpellBook.GetSpellBookItemInfo(spellSlot, spellType == "pet" and Enum.SpellBookSpellBank.Pet or Enum.SpellBookSpellBank.Player)
+ if spellBookItemInfo and spellBookItemInfo.actionID then
+ return spellBookItemInfo.actionID == spellID
+ end
+ end
+ -- Older API fallback
+ if br._G.GetSpellBookItemInfo then
+ local itemType, id = br._G.GetSpellBookItemInfo(spellSlot, (spellType == "pet" and Enum.SpellBookSpellBank.Pet or Enum.SpellBookSpellBank.Player))
+ if id == spellID then return true end
+ end
+ -- Fallback: try resolving via spell link/name
+ local spellName = (br._G.C_Spell and br._G.C_Spell.GetSpellName and br._G.C_Spell.GetSpellName(spellID)) or br.api.wow.GetSpellInfo(spellID)
+ if spellName then
+ local link = (br._G.C_Spell and br._G.C_Spell.GetSpellLink and br._G.C_Spell.GetSpellLink(spellName)) or br._G.GetSpellLink(spellID)
+ local currentSpellId = tonumber(link and link:gsub("|", "||"):match("spell:(%d+)"))
+ return currentSpellId == spellID
+ end
end
return false
end
-function br.getSpellInSpellBook(spellID, spellType)
- for i = 1, br._G.C_SpellBook.GetNumSpellBookSkillLines() do
- local skillLineInfo = br._G.C_SpellBook.GetSpellBookSkillLineInfo(i)
- local offset, numSlots = skillLineInfo.itemIndexOffset, skillLineInfo.numSpellBookItems
+function spell:getSpellInSpellBook(spellID, spellType)
+ for i = 1, br._G.GetNumSpellTabs() do
+ local name, texture, offset, numSlots, isGuild, offspecID = br._G.GetSpellTabInfo(i)
+ -- local offset, numSlots = skillLineInfo.itemIndexOffset, skillLineInfo.numSpellBookItems
for j = offset + 1, offset + numSlots do
- local spellBookItemInfo = br._G.C_SpellBook.GetSpellBookItemInfo(j,
- (spellType == "pet" and Enum.SpellBookSpellBank.Pet or Enum.SpellBookSpellBank.Player))
- local spellType, id = spellBookItemInfo.itemType, spellBookItemInfo.actionID
+ local spellType, id
+ if br._G.GetSpellBookItemInfo then
+ spellType, id = br._G.GetSpellBookItemInfo(j, (spellType == "pet" and Enum.SpellBookSpellBank.Pet or Enum.SpellBookSpellBank.Player))
+ elseif br._G.C_SpellBook and br._G.C_SpellBook.GetSpellBookItemInfo then
+ local info = br._G.C_SpellBook.GetSpellBookItemInfo(j, (spellType == "pet" and Enum.SpellBookSpellBank.Pet or Enum.SpellBookSpellBank.Player))
+ -- try to map modern return to older variables
+ if info then
+ spellType = info.itemType
+ id = info.actionID
+ end
+ end
+ -- local spellType, id = spellBookItemInfo.itemType, spellBookItemInfo.actionID
local spellName
if spellType == Enum.SpellBookItemType.Spell then
spellName = br._G.C_Spell.GetSpellName(id)
@@ -365,17 +400,330 @@ function br.getSpellInSpellBook(spellID, spellType)
return nil
end
--- if br.isKnown(106832) then
-function br.isKnown(spellID)
- local baseSpellID = br._G.FindBaseSpellByID(spellID)
- local _, _, spellInBookType = br.getSpellInSpellBook(spellID)
- return spellID ~= nil and spellInBookType ~= "Future Spell" and
- ( --[[spellInBookType ~= nil or]] br._G.IsPlayerSpell(tonumber(spellID))
- or br._G.IsSpellKnown(spellID) or br._G.IsPlayerSpell(tonumber(baseSpellID) or br._G.IsSpellKnown(baseSpellID))) -- or spellInBookType == "Spell")
+-- if br.functions.spell:isKnown(106832) then
+-- function spell:isKnown(spellID)
+-- local baseSpellID = br.api.wow.FindBaseSpellByID(spellID)
+-- local _, _, spellInBookType = br.functions.spell:getSpellInSpellBook(spellID)
+-- return spellID ~= nil and spellInBookType ~= "Future Spell" and
+-- ( --[[spellInBookType ~= nil or]] br._G.IsPlayerSpell(tonumber(spellID))
+-- or br._G.IsSpellKnown(spellID) or br._G.IsPlayerSpell(tonumber(baseSpellID) or br._G.IsSpellKnown(baseSpellID))) -- or spellInBookType == "Spell")
+-- end
+
+function spell:isKnown(spellID)
+ if spellID == nil then return false end
+
+ -- In Classic WoW, we need to check the specific spell ID, not just the name
+ -- because multiple ranks of the same spell have different IDs
+ if br.isClassic or br.isBC then
+ -- Check if this specific spell ID is known
+ return br._G.IsPlayerSpell(tonumber(spellID)) or br._G.IsSpellKnown(spellID) or br.functions.spell:isSpellInSpellbook(spellID, "spell")
+ end
+
+ -- For non-Classic, use the existing logic
+ local spellName = br.api.wow.GetSpellInfo(spellID)
+ local inBook = false
+ if br._G.GetSpellBookItemInfo then
+ inBook = br._G.GetSpellBookItemInfo(tostring(spellName)) ~= nil
+ else
+ -- Fallback to slot-based lookup if modern API missing
+ inBook = br.functions.spell:isSpellInSpellbook(spellID, "spell")
+ end
+ return inBook or br._G.IsPlayerSpell(tonumber(spellID)) or br._G.IsSpellKnown(spellID) or br.functions.spell:isSpellInSpellbook(spellID,"spell")
end
-function br.isActiveEssence(spellID)
- local _, _, heartIcon = br._G.GetSpellInfo(296208)
- local _, _, essenceIcon = br._G.GetSpellInfo(spellID)
+function spell:isActiveEssence(spellID)
+ local heartOfAzerothID = br.functions.spell:getHighestSpellID("heartOfAzeroth") or 296208
+ local _, _, heartIcon = br.api.wow.GetSpellInfo(heartOfAzerothID)
+ local _, _, essenceIcon = br.api.wow.GetSpellInfo(spellID)
return heartIcon == essenceIcon
end
+
+local function getEventBuffIDs()
+ return br.functions.spell:getHighestSpellID("forTheAlliance") or 193863,
+ br.functions.spell:getHighestSpellID("forTheHorde") or 193864
+end
+
+local function flipRace()
+ local race = select(2, br._G.UnitRace("player"))
+ local class = select(3, br._G.UnitClass("player"))
+ local forTheAllianceID, forTheHordeID = getEventBuffIDs()
+ if br.functions.aura:UnitBuffID("player", forTheAllianceID) then
+ if race == "Orc" then
+ return "Dwarf"
+ elseif race == "Undead" then
+ return "Human"
+ elseif race == "Troll" then
+ if class == 7 then
+ return "Draenei"
+ elseif class == 9 then
+ return "Human"
+ else
+ return "NightElf"
+ end
+ elseif race == "Tauren" then
+ if class == 11 then
+ return "NightElf"
+ else
+ return "Draenei"
+ end
+ elseif race == "BloodElf" then
+ if class == 12 then
+ return "NightElf"
+ else
+ return "Human"
+ end
+ elseif race == "Goblin" then
+ if class == 3 or class == 7 then
+ return "Dwarf"
+ else
+ return "Gnome"
+ end
+ elseif race == "Pandaren" then
+ return "Pandaren"
+ elseif race == "HighmountainTauren" then
+ if class == 11 then
+ return "NightElf"
+ else
+ return "Draenei"
+ end
+ elseif race == "Nightborne" then
+ if class == 9 then
+ return "Human"
+ else
+ return "NightElf"
+ end
+ elseif race == "MagharOrc" then
+ return "DarkIronDwarf"
+ end
+ elseif br.functions.aura:UnitBuffID("player", forTheHordeID) then
+ if race == "Worgen" then
+ return "Troll"
+ elseif race == "DarkIronDwarf" then
+ return "MagharOrc"
+ elseif race == "Human" then
+ if class == 2 then
+ return "BloodElf"
+ else
+ return "Undead"
+ end
+ elseif race == "NightElf" then
+ if class == 12 then
+ return "BloodElf"
+ else
+ return "Troll"
+ end
+ elseif race == "Dwarf" then
+ if class == 2 then
+ return "Tauren"
+ elseif class == 5 then
+ return "Undead"
+ else
+ return "Orc"
+ end
+ elseif race == "Draenei" then
+ if class == 8 then
+ return "Orc"
+ else
+ return "Tauren"
+ end
+ elseif race == "Pandaren" then
+ return "Pandaren"
+ elseif race == "Gnome" then
+ if class == 10 then
+ return "BloodElf"
+ else
+ return "Goblin"
+ end
+ elseif race == "VoidElf" then
+ if class == 3 or class == 5 or class == 1 then
+ return "BloodElf"
+ else
+ return "Troll"
+ end
+ elseif race == "LightforgedDraenei" then
+ if class == 8 then
+ return "Orc"
+ else
+ return "Tauren"
+ end
+ end
+ end
+end
+
+function spell:getRacial(thisRace)
+ local forTheAllianceID, forTheHordeID = getEventBuffIDs()
+ local forTheAlliance = br.functions.aura:UnitBuffID("player", forTheAllianceID) or false
+ local forTheHorde = br.functions.aura:UnitBuffID("player", forTheHordeID) or false
+ local race = select(2, br._G.UnitRace("player"))
+ if forTheAlliance or forTheHorde then
+ race = flipRace()
+ end
+
+ -- Read static racial IDs from the list; fall back to hardcoded values if the list isn't loaded yet.
+ local r = (br.lists and br.lists.racials) or {}
+
+ -- Three racials have IDs that vary by rank/level and must be resolved dynamically.
+ local BloodElfRacial, DraeneiRacial, OrcRacial
+ if race == "BloodElf" or race == thisRace then
+ local baseID = r._bloodElfBase or 69179
+ BloodElfRacial = select(7, br.api.wow.GetSpellInfo(br.api.wow.GetSpellInfo(baseID)))
+ end
+ if race == "Draenei" or race == thisRace then
+ local baseID = r._draeneiBase or 28880
+ DraeneiRacial = select(7, br.api.wow.GetSpellInfo(br.api.wow.GetSpellInfo(baseID)))
+ end
+ if race == "Orc" or race == thisRace then
+ local baseID = r._orcBase or 33702
+ OrcRacial = select(7, br.api.wow.GetSpellInfo(br.api.wow.GetSpellInfo(baseID)))
+ end
+
+ local racialSpells = {
+ -- Alliance
+ Dwarf = r.Dwarf, -- Stoneform
+ Gnome = r.Gnome, -- Escape Artist
+ Draenei = DraeneiRacial, -- Gift of the Naaru
+ Human = r.Human, -- Every Man for Himself
+ NightElf = r.NightElf, -- Shadowmeld
+ Worgen = r.Worgen, -- Darkflight
+ -- Horde
+ BloodElf = BloodElfRacial, -- Arcane Torrent
+ Goblin = r.Goblin, -- Rocket Barrage
+ Orc = OrcRacial, -- Blood Fury
+ Tauren = r.Tauren, -- War Stomp
+ Troll = r.Troll, -- Berserking
+ Scourge = r.Scourge, -- Will of the Forsaken
+ -- Both
+ Pandaren = r.Pandaren, -- Quaking Palm
+ -- Allied Races
+ HighmountainTauren = r.HighmountainTauren, -- Bull Rush
+ LightforgedDraenei = r.LightforgedDraenei, -- Light's Judgment
+ Nightborne = r.Nightborne, -- Arcane Pulse
+ VoidElf = r.VoidElf, -- Spatial Rift
+ DarkIronDwarf = r.DarkIronDwarf, -- Fireblood
+ MagharOrc = r.MagharOrc, -- Ancestral Call
+ ZandalariTroll = r.ZandalariTroll, -- Regeneratin'
+ KulTiran = r.KulTiran, -- Haymaker
+ Vulpera = r.Vulpera, -- Bag of Tricks
+ Mechagnome = r.Mechagnome, -- Hyper Organic Light Originator
+ }
+ local classicRacialSpells = {
+ Draenei = DraeneiRacial, -- Gift of the Naaru
+ NightElf = r.NightElf, -- Shadowmeld
+ }
+
+ if br.isClassic or br.isBC then
+ if thisRace ~= nil and classicRacialSpells[thisRace] ~= nil then return classicRacialSpells[thisRace] end
+ return classicRacialSpells[race]
+ else
+ if thisRace ~= nil and racialSpells[thisRace] ~= nil then return racialSpells[thisRace] end
+ return racialSpells[race]
+ end
+end
+
+-- ===========================
+-- SPELL RANK SYSTEM (CLASSIC)
+-- ===========================
+
+-- Get the highest known rank of a spell from a spell ID table or single ID
+-- @param spellIDOrTable - A spell ID (number) or table of spell IDs {rank1, rank2, ...}
+-- @return spellID - The highest rank spell ID that the player knows, or the original ID if not a table
+function spell:getHighestKnownRank(spellIDOrTable)
+ if not br.isClassic and not br.isBC then
+ return type(spellIDOrTable) == "table" and spellIDOrTable[1] or spellIDOrTable
+ end
+
+ -- If it's not a table, just return it
+ if type(spellIDOrTable) ~= "table" then
+ return spellIDOrTable
+ end
+
+ -- It's a table of ranks - find the highest known one
+ for i = #spellIDOrTable, 1, -1 do
+ local rankID = spellIDOrTable[i]
+ if self:isKnown(rankID) then
+ return rankID
+ end
+ end
+
+ -- No rank known, return the first rank (base)
+ return spellIDOrTable[1]
+end
+
+-- Get a specific rank of a spell
+-- @param spellIDOrTable - A spell ID (number) or table of spell IDs {rank1, rank2, ...}
+-- @param rank - The rank number (1-based index)
+-- @return spellID - The spell ID for the specified rank, or nil if doesn't exist
+function spell:getSpellRank(spellIDOrTable, rank)
+ if not br.isClassic and not br.isBC then
+ return type(spellIDOrTable) == "table" and spellIDOrTable[1] or spellIDOrTable
+ end
+
+ -- If it's not a table, return it for rank 1, nil otherwise
+ if type(spellIDOrTable) ~= "table" then
+ return rank == 1 and spellIDOrTable or nil
+ end
+
+ -- Return the specified rank if it exists
+ if rank >= 1 and rank <= #spellIDOrTable then
+ return spellIDOrTable[rank]
+ end
+
+ return nil
+end
+
+-- Get the number of ranks for a spell
+-- @param spellIDOrTable - A spell ID (number) or table of spell IDs
+-- @return number - The total number of ranks available
+function spell:getMaxRank(spellIDOrTable)
+ if type(spellIDOrTable) == "table" then
+ return #spellIDOrTable
+ end
+ return 1
+end
+
+-- Get the current rank the player knows for a spell
+-- @param spellIDOrTable - A spell ID (number) or table of spell IDs
+-- @return number - The highest rank number known (1-based), or 0 if none known
+function spell:getKnownRank(spellIDOrTable)
+ if not br.isClassic and not br.isBC then
+ return self:isKnown(type(spellIDOrTable) == "table" and spellIDOrTable[1] or spellIDOrTable) and 1 or 0
+ end
+
+ -- If it's not a table, check if we know it
+ if type(spellIDOrTable) ~= "table" then
+ return self:isKnown(spellIDOrTable) and 1 or 0
+ end
+
+ -- Find the highest known rank
+ for i = #spellIDOrTable, 1, -1 do
+ if self:isKnown(spellIDOrTable[i]) then
+ return i
+ end
+ end
+
+ return 0
+end
+
+-- Look up a spell ID from br.lists.spells by key name.
+-- Recursively searches all class/spec/type subtables for the key.
+-- If the found value is a rank table (Classic), returns the highest known rank.
+-- @param key - The spell name key as it appears in the spell list (e.g. "rake", "global")
+-- @return number|nil - The spell ID, or nil if the list is not loaded or the key is not found.
+function spell:getHighestSpellID(key)
+ if not br.lists or not br.lists.spells then return nil end
+ local function search(t)
+ for k, v in pairs(t) do
+ if k == key then
+ if type(v) == "table" then
+ return self:getHighestKnownRank(v)
+ end
+ return v
+ elseif type(v) == "table" then
+ local found = search(v)
+ if found then return found end
+ end
+ end
+ return nil
+ end
+ return search(br.lists.spells)
+end
\ No newline at end of file
diff --git a/System/Functions/Totem.lua b/System/Functions/Totem.lua
new file mode 100644
index 0000000000..6190ff616d
--- /dev/null
+++ b/System/Functions/Totem.lua
@@ -0,0 +1,177 @@
+local _, br = ...
+
+br.functions.totem = br.functions.totem or {}
+local totem = br.functions.totem
+
+local spellNameCache = {}
+
+local function resolveCandidateName(candidate)
+ if not candidate then return nil end
+
+ if type(candidate) == "number" then
+ return br.api.wow.GetSpellInfo(candidate)
+ end
+
+ if type(candidate) == "string" then
+ local idFromKey = br.player and br.player.spells and br.player.spells[candidate]
+ if type(idFromKey) == "number" then
+ return br.api.wow.GetSpellInfo(idFromKey)
+ end
+ return candidate
+ end
+
+ return nil
+end
+
+function totem:spellName(spellId)
+ if not spellId then return nil end
+ if spellNameCache[spellId] == nil then
+ spellNameCache[spellId] = br.api.wow.GetSpellInfo(spellId)
+ end
+ return spellNameCache[spellId]
+end
+
+function totem:getSlotInfo(slot)
+ if not slot then return false, nil, 0, 0, nil end
+ local haveTotem, name, startTime, duration, icon = br._G.GetTotemInfo(slot)
+ if not haveTotem then
+ return false, nil, 0, 0, nil
+ end
+ return true, name, startTime or 0, duration or 0, icon
+end
+
+function totem:slotRemains(slot)
+ local haveTotem, _, startTime, duration = self:getSlotInfo(slot)
+ if not haveTotem or duration <= 0 or startTime <= 0 then return 0 end
+ local remaining = (startTime + duration) - br._G.GetTime()
+ return remaining > 0 and remaining or 0
+end
+
+-- Candidates can be:
+-- - spell IDs (number)
+-- - spell keys from br.player.spells (string)
+-- - literal localized names (string)
+function totem:slotMatchesAny(slot, ...)
+ local haveTotem, activeName = self:getSlotInfo(slot)
+ if not haveTotem or not activeName or activeName == "" then return false end
+
+ for i = 1, select('#', ...) do
+ local candidate = select(i, ...)
+ local candidateName
+
+ candidateName = resolveCandidateName(candidate)
+
+ if candidateName and candidateName ~= "" and activeName == candidateName then
+ return true
+ end
+ end
+
+ return false
+end
+
+function totem:anySlotMatchesAny(...)
+ for slot = 1, 4 do
+ if self:slotMatchesAny(slot, ...) then
+ return true
+ end
+ end
+ return false
+end
+
+-- Returns the closest world object that matches the active totem name in this slot.
+-- @treturn any|nil object
+-- @treturn number|nil distance Player->totem distance in yards (raw 3D), if found
+-- @treturn string|nil name Active totem name for the slot
+function totem:getActiveObject(slot)
+ local haveTotem, name = self:getSlotInfo(slot)
+ if not haveTotem or not name or name == "" then return nil, nil, nil end
+ if not br.unlocked or not br._G.GetObjectCount or not br._G.GetObjectWithIndex then
+ return nil, nil, name
+ end
+
+ local pX, pY, pZ = br.functions.unit:GetObjectPosition("player")
+ if not pX or not pY or not pZ then return nil, nil, name end
+
+ local bestObj, bestDist
+ for i = 1, br._G.GetObjectCount() do
+ local obj = br._G.GetObjectWithIndex(i)
+ if obj and br.functions.unit:GetObjectExists(obj) then
+ local objName = br._G.UnitName(obj)
+ if objName == name then
+ local creator = br._G.UnitCreator and br._G.UnitCreator(obj) or nil
+ local isMine = creator == "player" or creator == "Player"
+ if not isMine and creator and br._G.UnitIsUnit then
+ isMine = br._G.UnitIsUnit(creator, "player")
+ end
+
+ if isMine then
+ local x, y, z = br.functions.unit:GetObjectPosition(obj)
+ if x and y and z then
+ local dx, dy, dz = x - pX, y - pY, z - pZ
+ local dist = math.sqrt((dx * dx) + (dy * dy) + (dz * dz))
+ if not bestDist or dist < bestDist then
+ bestObj, bestDist = obj, dist
+ end
+ end
+ end
+ end
+ end
+ end
+
+ return bestObj, bestDist, name
+end
+
+-- Returns player distance to the active totem in this slot.
+-- Returns 0 when no totem exists, or its object can't be located.
+function totem:slotDistance(slot)
+ local haveTotem = self:getSlotInfo(slot)
+ if not haveTotem then return 0 end
+ local _, dist = self:getActiveObject(slot)
+ return dist or 0
+end
+
+-- Returns the closest world object with the given localized name created by the player.
+function totem:getObjectByName(name)
+ if not name or name == "" then return nil, nil end
+ if not br.unlocked or not br._G.GetObjectCount or not br._G.GetObjectWithIndex then
+ return nil, nil
+ end
+
+ local pX, pY, pZ = br.functions.unit:GetObjectPosition("player")
+ if not pX or not pY or not pZ then return nil, nil end
+
+ local bestObj, bestDist
+ for i = 1, br._G.GetObjectCount() do
+ local obj = br._G.GetObjectWithIndex(i)
+ if obj and br.functions.unit:GetObjectExists(obj) and br._G.UnitName(obj) == name then
+ local creator = br._G.UnitCreator and br._G.UnitCreator(obj) or nil
+ local isMine = creator == "player" or creator == "Player"
+ if not isMine and creator and br._G.UnitIsUnit then
+ isMine = br._G.UnitIsUnit(creator, "player")
+ end
+
+ if isMine then
+ local x, y, z = br.functions.unit:GetObjectPosition(obj)
+ if x and y and z then
+ local dx, dy, dz = x - pX, y - pY, z - pZ
+ local dist = math.sqrt((dx * dx) + (dy * dy) + (dz * dz))
+ if not bestDist or dist < bestDist then
+ bestObj, bestDist = obj, dist
+ end
+ end
+ end
+ end
+ end
+
+ return bestObj, bestDist
+end
+
+-- Returns distance from player to a specific totem type (spell key/id/name).
+-- Returns 0 if the totem isn't active (any slot) or can't be located.
+function totem:distanceTo(candidate)
+ local name = resolveCandidateName(candidate)
+ if not name or name == "" then return 0 end
+ if not self:anySlotMatchesAny(candidate) then return 0 end
+ local _, dist = self:getObjectByName(name)
+ return dist or 0
+end
diff --git a/System/Functions/Unit.lua b/System/Functions/Unit.lua
index 22139a46ac..de8d3d2b54 100644
--- a/System/Functions/Unit.lua
+++ b/System/Functions/Unit.lua
@@ -1,75 +1,78 @@
local _, br = ...
-function br.GetObjectExists(Unit)
+br.functions.unit = br.functions.unit or {}
+local unit = br.functions.unit
+
+function unit:GetObjectExists(Unit)
if Unit == nil then return false end
- return br.GetUnitIsVisible(Unit)
+ return br.functions.unit:GetUnitIsVisible(Unit)
end
-function br.GetUnit(Unit)
- if Unit ~= nil and br.GetObjectExists(Unit) then
+function unit:GetUnit(Unit)
+ if Unit ~= nil and br.functions.unit:GetObjectExists(Unit) then
return Unit
end
return nil
end
-function br.GetUnitIsUnit(Unit, otherUnit)
- if not br.GetUnitIsVisible(Unit) or not br.GetUnitIsVisible(otherUnit) then return false end
+function unit:GetUnitIsUnit(Unit, otherUnit)
+ if not br.functions.unit:GetUnitIsVisible(Unit) or not br.functions.unit:GetUnitIsVisible(otherUnit) then return false end
return br._G.UnitIsUnit(Unit, otherUnit)
end
-function br.GetUnitReaction(Unit, otherUnit)
- if not br.GetUnitIsVisible(Unit) or not br.GetUnitIsVisible(otherUnit) then return 10 end
+function unit:GetUnitReaction(Unit, otherUnit)
+ if not br.functions.unit:GetUnitIsVisible(Unit) or not br.functions.unit:GetUnitIsVisible(otherUnit) then return 10 end
return br._G.UnitReaction(Unit, otherUnit)
end
-function br.GetUnitIsFriend(Unit, otherUnit)
- if not br.GetUnitIsVisible(Unit) or not br.GetUnitIsVisible(otherUnit) then return false end
+function unit:GetUnitIsFriend(Unit, otherUnit)
+ if not br.functions.unit:GetUnitIsVisible(Unit) or not br.functions.unit:GetUnitIsVisible(otherUnit) then return false end
return br._G.UnitIsFriend(Unit, otherUnit)
end
-function br.GetUnitExists(Unit)
+function unit:GetUnitExists(Unit)
if Unit == nil then return false end
return br._G.UnitExists(Unit)
end
-function br.GetUnitIsVisible(Unit)
+function unit:GetUnitIsVisible(Unit)
if Unit == nil then return false end
return br._G.UnitIsVisible(Unit)
end
-function br.GetUnitIsDeadOrGhost(Unit)
+function unit:GetUnitIsDeadOrGhost(Unit)
if Unit == nil then return false end
return br._G.UnitIsDeadOrGhost(Unit)
end
-function br.GetObjectFacing(Unit)
- if br.unlocked and br.GetObjectExists(Unit) then
+function unit:GetObjectFacing(Unit)
+ if br.unlocked and br.functions.unit:GetObjectExists(Unit) then
return br._G.ObjectFacing(Unit)
else
return 0
end
end
-function br.GetObjectPosition(Unit)
+function unit:GetObjectPosition(Unit)
local x, y, z = 0, 0, 0
if br.unlocked then --EWT then
if Unit == nil then return br._G.ObjectPosition("player") end
- if br.GetObjectExists(Unit) then
+ if br.functions.unit:GetObjectExists(Unit) then
x, y, z = br._G.ObjectPosition(Unit)
end
end
return x, y, z
end
-function br.GetObjectType(Unit)
- if br.unlocked and br.GetObjectExists(Unit) then
+function unit:GetObjectType(Unit)
+ if br.unlocked and br.functions.unit:GetObjectExists(Unit) then
return br._G.ObjectTypes(Unit)
else
return 65561
end
end
-function br.GetObjectIndex(Index)
- if br.unlocked and br.GetObjectExists(br._G.GetObjectWithIndex(Index)) then
+function unit:GetObjectIndex(Index)
+ if br.unlocked and br.functions.unit:GetObjectExists(br._G.GetObjectWithIndex(Index)) then
return br._G.GetObjectWithIndex(Index)
else
return 0
@@ -83,15 +86,15 @@ end
-- return 0
-- end
-- end
-function br.GetObjectID(Unit)
- if br.unlocked and br.GetObjectExists(Unit) then
+function unit:GetObjectID(Unit)
+ if br.unlocked and br.functions.unit:GetObjectExists(Unit) then
return br._G.ObjectID(Unit)
else
return 0
end
end
-function br.UnitIsTappedByPlayer(mob)
+function unit:UnitIsTappedByPlayer(mob)
-- if br._G.UnitTarget("player") and mob == br._G.UnitTarget("player") then return true end
-- if UnitAffectingCombat(mob) and br._G.UnitTarget(mob) then
-- mobPlaceHolderOne = br._G.UnitTarget(mob)
@@ -106,24 +109,34 @@ function br.UnitIsTappedByPlayer(mob)
end
end
-function br.getSpellUnit(spellCast, aoe, minRange, maxRange, spellType)
- local spellName = br._G.GetSpellInfo(spellCast)
+function unit:getSpellUnit(spellCast, aoe, minRange, maxRange, spellType)
+ local spellName = br.api.wow.GetSpellInfo(spellCast)
if aoe == nil then aoe = false end
local hasRange = br._G.C_Spell.SpellHasRange(spellName) and true or false
local facing = not aoe
- local unit = br.dynamicTarget(maxRange, facing) or (not hasRange and "player")
- if not unit then return "None" end
- local distance = br.getDistance(unit)
- local thisUnit = "None"
- if (distance >= minRange and distance < maxRange) then
+ local unit = maxRange > 0 and br.engines.enemiesEngineFunctions:dynamicTarget(maxRange, facing) or nil
+
+ -- Handle nil from dynamicTarget
+ -- if not unit then
+ -- -- Fallback to player for non-ranged spells only
+ -- if not hasRange then
+ -- return "player"
+ -- end
+ -- return "None"
+ -- end
+
+ local distance = unit and br.functions.range:getDistance(unit) or 0
+ if not distance then return "None" end
+
+ if (distance >= minRange and distance < maxRange) or (minRange > maxRange) then
if spellType == "Helpful" then return "player" end
if hasRange then return unit else return "player" end
end
- return thisUnit
+ return "None"
end
-- if getCreatureType(Unit) == true then
-function br.getCreatureType(Unit)
+function unit:getCreatureType(Unit)
local CreatureTypeList = { "Critter", "Totem", "Non-combat Pet", "Wild Pet" }
for i = 1, #CreatureTypeList do
if br._G.UnitCreatureType(Unit) == CreatureTypeList[i] then
@@ -137,49 +150,47 @@ function br.getCreatureType(Unit)
end
end
--- if br.getFacing("target","player") == false then
-function br.getFacing(Unit1, Unit2, Degrees)
- if Degrees == nil then
- Degrees = 90
- end
- if Unit2 == nil then
- Unit2 = "player"
- end
- if br.GetObjectExists(Unit1) and br.GetUnitIsVisible(Unit1) and br.GetObjectExists(Unit2) and br.GetUnitIsVisible(Unit2) then
- local angle3
- local angle1 = br.GetObjectFacing(Unit1)
- local angle2 = br.GetObjectFacing(Unit2)
- local Y1, X1, Z1 = br.GetObjectPosition(Unit1)
- local Y2, X2, Z2 = br.GetObjectPosition(Unit2)
- if Y1 and X1 and Z1 and angle1 and Y2 and X2 and Z2 and angle2 then
- local deltaY = Y2 - Y1
- local deltaX = X2 - X1
- angle1 = math.deg(math.abs(angle1 - math.pi * 2))
- if deltaX > 0 then
- angle2 = math.deg(math.atan(deltaY / deltaX) + (math.pi / 2) + math.pi)
- elseif deltaX < 0 then
- angle2 = math.deg(math.atan(deltaY / deltaX) + (math.pi / 2))
- end
- if angle2 - angle1 > 180 then
- angle3 = math.abs(angle2 - angle1 - 360)
- elseif angle1 - angle2 > 180 then
- angle3 = math.abs(angle1 - angle2 - 360)
- else
- angle3 = math.abs(angle2 - angle1)
- end
- -- return angle3
- if angle3 < Degrees then
- return true
- else
- return false
- end
- end
+-- if br.functions.unit:getFacing("target","player") == false then
+function unit:getFacing(Unit1, Unit2, Degrees)
+ if Degrees == nil then Degrees = 90 end
+ if Unit2 == nil then Unit2 = "player" end
+
+ -- We can only do accurate facing math when the unlocker APIs are available.
+ if not br.unlocked then
+ return nil
+ end
+ if not (br.functions.unit:GetObjectExists(Unit1) and br.functions.unit:GetUnitIsVisible(Unit1)
+ and br.functions.unit:GetObjectExists(Unit2) and br.functions.unit:GetUnitIsVisible(Unit2)) then
+ return nil
end
+
+ -- Prefer unlocker-provided implementation when present.
+ -- Note: our Degrees parameter is treated as a HALF-angle (e.g. 90 == 180deg front hemisphere).
+ if br._G.ObjectIsFacing ~= nil then
+ local ok, result = pcall(br._G.ObjectIsFacing, Unit1, Unit2, Degrees * 2)
+ if not ok then return nil end
+ return result == true
+ end
+
+ -- Fallback: compute shortest angle between Unit1 facing and the vector Unit1->Unit2.
+ local facing = br.functions.unit:GetObjectFacing(Unit1)
+ local x1, y1, z1 = br.functions.unit:GetObjectPosition(Unit1)
+ local x2, y2, z2 = br.functions.unit:GetObjectPosition(Unit2)
+ if facing == nil or x1 == nil or y1 == nil or x2 == nil or y2 == nil then
+ return nil
+ end
+
+ local angleTo = math.atan2(y2 - y1, x2 - x1) % (math.pi * 2)
+ local diff = math.abs(facing - angleTo)
+ if diff > math.pi then
+ diff = (math.pi * 2) - diff
+ end
+ return diff < math.rad(Degrees)
end
-function br.getGUID(unit)
+function unit:getGUID(unit)
local nShortHand, targetGUID = "", ""
- if br.GetObjectExists(unit) then
+ if br.functions.unit:GetObjectExists(unit) then
local guid = br._G.UnitGUID(unit)
if guid ~= nil then
if br._G.UnitIsPlayer(unit) then
@@ -194,21 +205,21 @@ function br.getGUID(unit)
return targetGUID, nShortHand
end
--- if br.getHP("player") then
-function br.getHP(Unit)
- if br.GetObjectExists(Unit) then
+-- if br.functions.unit:getHP("player") then
+function unit:getHP(Unit)
+ if br.functions.unit:GetObjectExists(Unit) then
if br._G.UnitIsEnemy("player", Unit) then
return 100 * (br._G.UnitHealth(Unit) / br._G.UnitHealthMax(Unit))
else
- if not br.GetUnitIsDeadOrGhost(Unit) and br.GetUnitIsVisible(Unit) then
- for i = 1, #br.friend do
- if br.friend[i] then
- if br.friend[i].guidsh == string.sub(br._G.UnitGUID(Unit), -5) then
- return br.friend[i].hp
+ if not br.functions.unit:GetUnitIsDeadOrGhost(Unit) and br.functions.unit:GetUnitIsVisible(Unit) then
+ for i = 1, #br.engines.healingEngine.friend do
+ if br.engines.healingEngine.friend[i] then
+ if br.engines.healingEngine.friend[i].guidsh == string.sub(br._G.UnitGUID(Unit), -5) then
+ return br.engines.healingEngine.friend[i].hp
end
end
end
- if br.getOptionCheck("Incoming Heals") == true and br._G.UnitGetIncomingHeals(Unit, "player") ~= nil then
+ if br.functions.misc:getOptionCheck("Incoming Heals") == true and br._G.UnitGetIncomingHeals(Unit, "player") ~= nil then
return 100 * (br._G.UnitHealth(Unit) + br._G.UnitGetIncomingHeals(Unit, "player")) /
br._G.UnitHealthMax(Unit)
else
@@ -220,13 +231,13 @@ function br.getHP(Unit)
return 0
end
--- if br.getHPLossPercent("player",5) then
-function br.getHPLossPercent(unit, sec)
+-- if br.functions.unit:getHPLossPercent("player",5) then
+function unit:getHPLossPercent(unit, sec)
if unit == nil then unit = "player" end
- local currentHP = br.getHP(unit)
+ local currentHP = br.functions.unit:getHP(unit)
if sec == nil then sec = 1 end
if br.snapHP == nil then br.snapHP = 0 end
- if br.timer:useTimer("Loss Percent", sec) then
+ if br.debug.timer:useTimer("Loss Percent", sec) then
br.snapHP = currentHP
end
if br.snapHP < currentHP then
@@ -236,15 +247,15 @@ function br.getHPLossPercent(unit, sec)
end
end
-function br.getLowestUnit(range)
+function unit:getLowestUnit(range)
local lowestUnit = "player"
- local lowestHP = br.getHP("player")
+ local lowestHP = br.functions.unit:getHP("player")
if range == nil then range = 40 end
- if br ~= nil and br.friend ~= nil then
- for i = 1, #br.friend do
- local thisUnit = br.friend[i].unit
- local thisDist = br.getDistance(thisUnit)
- local thisHp = br.getHP(thisUnit)
+ if br ~= nil and br.engines.healingEngine.friend ~= nil then
+ for i = 1, #br.engines.healingEngine.friend do
+ local thisUnit = br.engines.healingEngine.friend[i].unit
+ local thisDist = br.functions.range:getDistance(thisUnit)
+ local thisHp = br.functions.unit:getHP(thisUnit)
if thisDist < range and thisHp < lowestHP and thisHp > 0 then
lowestUnit = thisUnit
lowestHP = thisHp
@@ -255,68 +266,87 @@ function br.getLowestUnit(range)
end
-- if getBossID("boss1") == 71734 then
-function br.getBossID(BossUnitID)
- return br.GetObjectID(BossUnitID)
+function unit:getBossID(BossUnitID)
+ return br.functions.unit:GetObjectID(BossUnitID)
end
-function br.getUnitID(Unit)
- if br.GetObjectExists(Unit) and br.GetUnitIsVisible(Unit) then
+function unit:getUnitID(Unit)
+ if br.functions.unit:GetObjectExists(Unit) and br.functions.unit:GetUnitIsVisible(Unit) then
local id = select(6, br._G.strsplit("-", br._G.UnitGUID(Unit) or ""))
return tonumber(id)
end
return 0
end
-function br.isAberration(Unit)
+function unit:isAberration(Unit)
if Unit == nil then Unit = "target" end
return br._G.UnitCreatureType(Unit) == "Aberration"
end
--- if br.isAlive([Unit]) == true then
-function br.isAlive(Unit)
+-- if br.functions.unit:isAlive([Unit]) == true then
+function unit:isAlive(Unit)
Unit = Unit or "player"
if br._G.UnitIsDeadOrGhost(Unit) == false then
return true
end
end
-function br.isInstanceBoss(unit)
- if br._G.IsInInstance() then
- local _, _, encountersTotal = br._G.GetInstanceLockTimeRemaining();
- for i = 1, encountersTotal do
- if unit == "player" then
- local bossList = br._G.GetInstanceLockTimeRemainingEncounter(i)
- br._G.print(bossList)
- end
- if br.GetObjectExists(unit) then
- local bossName = br._G.GetInstanceLockTimeRemainingEncounter(i)
- local targetName = br._G.UnitName(unit)
- -- Print("Target: "..targetName.." | Boss: "..bossName.." | Match: "..tostring(targetName == bossName))
- if targetName == bossName then return true end
+function unit:isInstanceBoss(unit)
+ unit = unit or "target"
+ if not br.functions.unit:GetObjectExists(unit) then return false end
+
+ if br._G.IsInInstance and br._G.IsInInstance() then
+ -- Try modern instance API first (may exist on Retail or newer clients)
+ if br._G.GetInstanceLockTimeRemaining then
+ local ok, _, _, encountersTotal = pcall(br._G.GetInstanceLockTimeRemaining)
+ if ok and type(encountersTotal) == "number" and encountersTotal > 0 then
+ for i = 1, encountersTotal do
+ local ok2, bossName = pcall(br._G.GetInstanceLockTimeRemainingEncounter, i)
+ if ok2 and bossName and br.functions.unit:GetObjectExists(unit) then
+ local targetName = br._G.UnitName(unit)
+ if targetName == bossName then return true end
+ end
+ end
end
end
+
+ -- Always fall back to boss1..boss5 unit frames (works in Classic and Retail)
for i = 1, 5 do
local bossNum = "boss" .. i
- if br.GetUnitIsUnit(bossNum, unit) then return true end
+ if br.functions.unit:GetUnitIsUnit(bossNum, unit) then return true end
end
+
+ -- Extra heuristics
+ local class = br._G.UnitClassification(unit)
+ if class == "worldboss" then return true end
+ local level = br._G.UnitLevel(unit)
+ if level and level < 0 then return true end
end
return false
end
--- br.isBoss()
-function br.isBoss(unit)
- if unit == nil then unit = "target" end
- if br.GetObjectExists(unit) and not br.isTotem(unit) then
- local class = br._G.UnitClassification(unit)
- local healthMax = br._G.UnitHealthMax(unit)
- local pHealthMax = br._G.UnitHealthMax("player")
- local instance = select(2, br._G.IsInInstance())
- return br.isInstanceBoss(unit) or br.isDummy(unit)
- or (not br.isChecked("Boss Detection Only In Instance") and not br._G.UnitIsTrivial(unit) and instance ~= "party"
- and ((class == "rare" and healthMax > 4 * pHealthMax) or class == "rareelite" or class == "worldboss"
- or (class == "elite" and ((healthMax > 4 * pHealthMax and instance ~= "raid") or instance == "scenario")) or br._G.UnitLevel(unit) < 0))
+-- br.functions.unit:isBoss()
+function unit:isBoss(unit)
+ unit = unit or "target"
+ if not br.functions.unit:GetObjectExists(unit) or br.engines.enemiesEngineFunctions:isTotem(unit) then return false end
+
+ -- Quick exact-match check using static boss ID list
+ if br.lists and br.lists.bossIds then
+ local id = br.functions.unit:getUnitID(unit)
+ if id and id > 0 and br.lists.bossIds[id] then
+ return true
+ end
end
- return false
+
+ local class = br._G.UnitClassification(unit)
+ local healthMax = br._G.UnitHealthMax(unit) or 0
+ local pHealthMax = br._G.UnitHealthMax("player") or 1
+ local instance = select(2, br._G.IsInInstance())
+
+ return br.functions.unit:isInstanceBoss(unit) or br.functions.unit:isDummy(unit)
+ or (not br.functions.misc:isChecked("Boss Detection Only In Instance") and not br._G.UnitIsTrivial(unit) and instance ~= "party"
+ and ((class == "rare" and healthMax > 4 * pHealthMax) or class == "rareelite" or class == "worldboss"
+ or (class == "elite" and ((healthMax > 4 * pHealthMax and instance ~= "raid") or instance == "scenario")) or (br._G.UnitLevel(unit) and br._G.UnitLevel(unit) < 0)))
end
local critterTypes = {
@@ -334,12 +364,12 @@ local critterTypes = {
["Ungezähmtes Tier"] = true,
["Mascotte sauvage"] = true
}
-function br.isCritter(Unit)
+function unit:isCritter(Unit)
if Unit == nil then Unit = "target" end
return critterTypes[br._G.UnitCreatureType(Unit)] or false
end
-function br.isDemon(Unit)
+function unit:isDemon(Unit)
if Unit == nil then Unit = "target" end
local isDemon = false
local unitType = br._G.UnitCreatureType(Unit)
@@ -361,11 +391,11 @@ function br.isDemon(Unit)
return isDemon
end
-function br.isExplosive(Unit)
- return br.GetObjectID(Unit) == 120651
+function unit:isExplosive(Unit)
+ return br.functions.unit:GetObjectID(Unit) == 120651
end
-function br.isUndead(Unit)
+function unit:isUndead(Unit)
if Unit == nil then Unit = "target" end
local isUndead = false
local unitType = br._G.UnitCreatureType(Unit)
@@ -387,7 +417,7 @@ function br.isUndead(Unit)
return isUndead
end
-function br.isBeast(Unit)
+function unit:isBeast(Unit)
if Unit == nil then Unit = "target" end
local isBeast = false
local unitType = br._G.UnitCreatureType(Unit)
@@ -408,7 +438,7 @@ function br.isBeast(Unit)
return isBeast
end
-function br.isHumanoid(Unit)
+function unit:isHumanoid(Unit)
if Unit == nil then Unit = "target" end
local isHumanoid = false
local unitType = br._G.UnitCreatureType(Unit)
@@ -428,13 +458,13 @@ function br.isHumanoid(Unit)
end
-- Dummy Check
-function br.isDummy(Unit)
+function unit:isDummy(Unit)
if Unit == nil then
Unit = "target"
end
- if br.GetObjectExists(Unit) then
+ if br.functions.unit:GetObjectExists(Unit) then
local dummies = br.lists.dummies
- local objectID = br.GetObjectID(Unit)
+ local objectID = br.functions.unit:GetObjectID(Unit)
-- if dummies[tonumber(string.match(UnitGUID(Unit),"-(%d+)-%x+$"))] then --~= nil
return dummies[objectID] ~= nil
end
@@ -442,7 +472,7 @@ function br.isDummy(Unit)
end
-- if isEnemy([Unit])
-function br.isEnemy(Unit)
+function unit:isEnemy(Unit)
Unit = Unit or "target"
if br._G.UnitCanAttack(Unit, "player") then
return true
@@ -451,14 +481,133 @@ function br.isEnemy(Unit)
end
end
-function br.isTankInRange()
- if #br.friend > 1 then
- for i = 1, #br.friend do
- local friend = br.friend[i]
- if friend.GetRole() == "TANK" and not br.GetUnitIsDeadOrGhost(friend.unit) and br.getDistance(friend.unit) < 40 then
+function unit:isTankInRange(range)
+ if range == nil then range = 40 end
+ if #br.engines.healingEngine.friend > 1 then
+ for i = 1, #br.engines.healingEngine.friend do
+ local friend = br.engines.healingEngine.friend[i]
+ if friend.GetRole() == "TANK" and not br.functions.unit:GetUnitIsDeadOrGhost(friend.unit) and br.functions.range:getDistance(friend.unit) < range then
return true, friend.unit
end
end
end
return false
end
+
+function unit:isFleeing(thisUnit)
+ if thisUnit == nil then thisUnit = "target" end
+
+ -- Early exit if unit doesn't exist or is dead
+ if not unit:GetObjectExists(thisUnit) or unit:GetUnitIsDeadOrGhost(thisUnit) then
+ return false
+ end
+
+ -- Don't check bosses (they typically don't flee)
+ if unit:isBoss(thisUnit) then
+ return false
+ end
+
+ -- Health-based fleeing detection (10-25% health range)
+ local unitHP = unit:getHP(thisUnit)
+ local lowHealthThreshold = unitHP <= 25 and unitHP > 0
+
+ -- Movement pattern detection
+ local isMovingAway = false
+ local unitSpeed = br._G.GetUnitSpeed(thisUnit)
+ if unitSpeed > 0 then
+ -- Check if unit is facing away from player (indicating fleeing)
+ local isFacingPlayer = unit:getFacing(thisUnit, "player")
+ if not isFacingPlayer then
+ isMovingAway = true
+ end
+ end
+
+ -- Return true if any fleeing condition is met
+ return --[[lowHealthThreshold and]] isMovingAway
+end
+
+function unit:isElemental(Unit)
+ if Unit == nil then Unit = "target" end
+ local isElemental = false
+ local unitType = br._G.UnitCreatureType(Unit)
+ local types = {
+ "Elemental",
+ "Elementar",
+ "Elemental", -- ES/PT
+ "Élémentaire",
+ "Elementare",
+ "Элементаль",
+ "정령",
+ "元素生物",
+ "元素生物"
+ }
+ for i = 1, #types do
+ if types[i] == unitType then isElemental = true end
+ end
+ return isElemental
+end
+
+-- if br.functions.unit:isEating([Unit]) == true then
+function unit:isEating(Unit)
+ Unit = Unit or "player"
+ local eatingBuffNames = {
+ ["Eating"] = true, -- enUS
+ ["Food"] = true, -- enUS (food buff)
+ ["Essen"] = true, -- deDE
+ ["Speise"] = true, -- deDE (food buff)
+ ["Nahrung"] = true, -- deDE (alt)
+ ["Manger"] = true, -- frFR
+ ["Nourriture"] = true, -- frFR (food buff)
+ ["Comiendo"] = true, -- esES / esMX
+ ["Comida"] = true, -- esES / esMX / ptBR (food buff)
+ ["Comendo"] = true, -- ptBR
+ ["Alimento"] = true, -- ptBR (food buff)
+ ["Mangiando"] = true, -- itIT
+ ["Cibo"] = true, -- itIT (food buff)
+ ["Поглощение пищи"] = true, -- ruRU
+ ["Еда"] = true, -- ruRU (food buff)
+ ["Пища"] = true, -- ruRU (alt)
+ ["음식 먹는 중"] = true, -- koKR
+ ["음식"] = true, -- koKR (food buff)
+ ["进食中"] = true, -- zhCN
+ ["食物"] = true, -- zhCN / zhTW (food buff)
+ ["進食中"] = true, -- zhTW
+ }
+ for i = 1, 40 do
+ local name = br.functions.aura:UnitBuff(Unit, i)
+ if not name then break end
+ if eatingBuffNames[name] then return true end
+ end
+ return false
+end
+
+-- if br.functions.unit:isDrinking([Unit]) == true then
+function unit:isDrinking(Unit)
+ Unit = Unit or "player"
+ local drinkingBuffNames = {
+ ["Drinking"] = true, -- enUS
+ ["Drink"] = true, -- enUS (drink buff)
+ ["Trinken"] = true, -- deDE
+ ["Getränk"] = true, -- deDE (drink buff)
+ ["Boire"] = true, -- frFR
+ ["Boisson"] = true, -- frFR (drink buff)
+ ["Bebiendo"] = true, -- esES / esMX
+ ["Bebida"] = true, -- esES / esMX / ptBR (drink buff)
+ ["Bebendo"] = true, -- ptBR
+ ["Bevendo"] = true, -- itIT
+ ["Bevanda"] = true, -- itIT (drink buff)
+ ["Употребление напитка"] = true, -- ruRU
+ ["Напиток"] = true, -- ruRU (drink buff)
+ ["음료 마시는 중"] = true, -- koKR
+ ["음료"] = true, -- koKR (drink buff)
+ ["喝水中"] = true, -- zhCN / zhTW
+ ["饮料"] = true, -- zhCN (drink buff)
+ ["飲料"] = true, -- zhTW (drink buff)
+ }
+ for i = 1, 40 do
+ local name = br.functions.aura:UnitBuff(Unit, i)
+ if not name then break end
+ if drinkingBuffNames[name] then return true end
+ end
+ return false
+end
\ No newline at end of file
diff --git a/System/Lists/Bosses.lua b/System/Lists/Bosses.lua
new file mode 100644
index 0000000000..4d7f098a58
--- /dev/null
+++ b/System/Lists/Bosses.lua
@@ -0,0 +1,1689 @@
+local _, br = ...
+if br.lists == nil then br.lists = {} end
+
+--[[
+ LibBossIDs-1.0 (trimmed/adapted)
+ Source: LibBossIDs-1.0 by Elsia (https://www.wowace.com)
+ This file preserves the original section comments and boss name notes.
+ The original library exposed `lib.BossIDs = BossIDs`; here we populate
+ `br.lists.bossIds` directly to be used by BadRotations.
+]]
+
+br.lists.bossIds = {
+ -------------------------------------------------------------------------------
+ -- Abyssal Maw: Throne of the Tides
+ -------------------------------------------------------------------------------
+ [40586] = true, -- Lady Naz'jar
+ [40765] = true, -- Commander Ulthok
+ [40825] = true, -- Erunak Stonespeaker
+ [40788] = true, -- Mindbender Ghur'sha
+ [42172] = true, -- Ozumat? Not in heroic! /Mikk
+ [44566] = true, -- Ozumat - confirmed in heroic! /Mikk
+
+ -------------------------------------------------------------------------------
+ -- Ahn'kahet: The Old Kingdom
+ -------------------------------------------------------------------------------
+ [29309] = true, -- Elder Nadox
+ [29308] = true, -- Prince Taldaram (Ahn'kahet: The Old Kingdom)
+ [29310] = true, -- Jedoga Shadowseeker
+ [29311] = true, -- Herald Volazj
+ [30258] = true, -- Amanitar (Heroic)
+
+ -------------------------------------------------------------------------------
+ -- Auchindoun
+ -------------------------------------------------------------------------------
+ [75839] = true, -- Vigilant Kaathar
+ [76177] = true, -- Soulbinder Nyami
+ [75927] = true, -- Azzakel
+ [77734] = true, -- Teron'gor
+
+ -------------------------------------------------------------------------------
+ -- Auchindoun: Auchenai Crypts
+ -------------------------------------------------------------------------------
+ [18371] = true, -- Shirrak the Dead Watcher
+ [18373] = true, -- Exarch Maladaar
+
+ -------------------------------------------------------------------------------
+ -- Auchindoun: Mana-Tombs
+ -------------------------------------------------------------------------------
+ [18341] = true, -- Pandemonius
+ [18343] = true, -- Tavarok
+ [22930] = true, -- Yor (Heroic)
+ [18344] = true, -- Nexus-Prince Shaffar
+
+ -------------------------------------------------------------------------------
+ -- Auchindoun: Sethekk Halls
+ -------------------------------------------------------------------------------
+ [18472] = true, -- Darkweaver Syth
+ [23035] = true, -- Anzu (Heroic)
+ [18473] = true, -- Talon King Ikiss
+
+ -------------------------------------------------------------------------------
+ -- Auchindoun: Shadow Labyrinth
+ -------------------------------------------------------------------------------
+ [18731] = true, -- Ambassador Hellmaw
+ [18667] = true, -- Blackheart the Inciter
+ [18732] = true, -- Grandmaster Vorpil
+ [18708] = true, -- Murmur
+
+ -------------------------------------------------------------------------------
+ -- Azjol-Nerub
+ -------------------------------------------------------------------------------
+ [28684] = true, -- Krik'thir the Gatewatcher
+ [28921] = true, -- Hadronox
+ [29120] = true, -- Anub'arak
+
+ -------------------------------------------------------------------------------
+ -- Azshara
+ -------------------------------------------------------------------------------
+ [14464] = true, -- Avalanchion
+ [6109] = true, -- Azuregos
+
+ -------------------------------------------------------------------------------
+ -- Black Temple
+ -------------------------------------------------------------------------------
+ [22887] = true, -- High Warlord Naj'entus
+ [22898] = true, -- Supremus
+ [22841] = true, -- Shade of Akama
+ [22871] = true, -- Teron Gorefiend
+ [22948] = true, -- Gurtogg Bloodboil
+ [23420] = true, -- Essence of Anger
+ [23419] = true, -- Essence of Desire
+ [23418] = true, -- Essence of Suffering
+ [22947] = true, -- Mother Shahraz
+ [23426] = true, -- Illidari Council
+ [22917] = true, -- Illidan Stormrage -- Not adding solo quest IDs for now
+ [22949] = true, -- Gathios the Shatterer
+ [22950] = true, -- High Nethermancer Zerevor
+ [22951] = true, -- Lady Malande
+ [22952] = true, -- Veras Darkshadow
+
+ -------------------------------------------------------------------------------
+ -- Baradin Hold
+ -------------------------------------------------------------------------------
+ [47120] = true, -- Argaloth
+ [52363] = true, -- Occu'thar
+ [55869] = true, -- Alizabal (4.3)
+
+ -------------------------------------------------------------------------------
+ -- Blackfathom Deeps
+ -------------------------------------------------------------------------------
+ [4829] = true, -- Aku'mai
+ [4830] = true, -- Old Serra'kis
+ [4831] = true, -- Lady Sarevess
+ [4832] = true, -- Twilight Lord Kelris
+ [4887] = true, -- Ghamoo-ra
+ [6243] = true, -- Gelihast
+ [12876] = true, -- Baron Aquanis
+ [12902] = true, -- Lorgus Jett
+
+ -------------------------------------------------------------------------------
+ -- Blackrock Depths: Detention Block
+ -------------------------------------------------------------------------------
+ [9018] = true, -- High Interrogator Gerstahn
+
+ -------------------------------------------------------------------------------
+ -- Blackrock Depths: Grim Guzzler
+ -------------------------------------------------------------------------------
+ [9543] = true, -- Ribbly Screwspigot
+ [9537] = true, -- Hurley Blackbreath
+ [9502] = true, -- Phalanx
+ [9499] = true, -- Plugger Spazzring
+ [23872] = true, -- Coren Direbrew
+
+ -------------------------------------------------------------------------------
+ -- Blackrock Depths: Halls of the Law
+ -------------------------------------------------------------------------------
+ [9025] = true, -- Lord Roccor
+ [9319] = true, -- Houndmaster Grebmar
+
+ -------------------------------------------------------------------------------
+ -- Blackrock Depths: Inner Blackrock Depths
+ -------------------------------------------------------------------------------
+ [9156] = true, -- Ambassador Flamelash
+ [8923] = true, -- Panzor the Invincible
+ [9035] = true, -- Anger'rel
+ [9039] = true, -- Doom'rel
+ [9040] = true, -- Dope'rel
+ [9037] = true, -- Gloom'rel
+ [9034] = true, -- Hate'rel
+ [9038] = true, -- Seeth'rel
+ [9036] = true, -- Vile'rel
+ [9938] = true, -- Magmus
+ [10076] = true, -- High Priestess of Thaurissan
+ [8929] = true, -- Princess Moira Bronzebeard
+ [9019] = true, -- Emperor Dagran Thaurissan
+
+ -------------------------------------------------------------------------------
+ -- Blackrock Depths: Outer Blackrock Depths
+ -------------------------------------------------------------------------------
+ [9024] = true, -- Pyromancer Loregrain
+ [9041] = true, -- Warder Stilgiss
+ [9042] = true, -- Verek
+ [9476] = true, -- Watchman Doomgrip
+
+ [9056] = true, -- Fineous Darkvire
+ [9017] = true, -- Lord Incendius
+ [9016] = true, -- Bael'Gar
+ [9033] = true, -- General Angerforge
+ [8983] = true, -- Golem Lord Argelmach
+ -- Dark Keepers, 6 of em: http://www.wowpedia.org/Dark_Keeper
+
+ -------------------------------------------------------------------------------
+ -- Blackrock Depths: Ring of Law (Arena)
+ -------------------------------------------------------------------------------
+ [9031] = true, -- Anub'shiah
+ [9029] = true, -- Eviscerator
+ [9027] = true, -- Gorosh the Dervish
+ [9028] = true, -- Grizzle
+ [9032] = true, -- Hedrum the Creeper
+ [9030] = true, -- Ok'thor the Breaker
+ [16059] = true, -- Theldren
+
+ -------------------------------------------------------------------------------
+ -- Blackrock Foundry
+ -------------------------------------------------------------------------------
+ -- Slagworks
+ [76877] = true, -- Gruul
+ [77182] = true, -- Oregorger
+ [76806] = true, -- Blast Furnace - Heart of the Mountain
+ [76809] = true, -- Blast Furnace - Foreman Feldspar
+ -- The Black Forge
+ [76973] = true, -- Hans'gar & Franzok - Hans'gar
+ [76974] = true, -- Hans'gar & Franzok - Franzok
+ [76814] = true, -- Flamebender Ka'graz
+ [77692] = true, -- Kromog
+ -- Iron Assembly
+ [76865] = true, -- Beastlord Darmac
+ [76906] = true, -- Operator Thogar
+ [77557] = true, -- Iron Maidens - Admiral Gar'an
+ [77231] = true, -- Iron Maidens - Enforcer Sorka
+ [77477] = true, -- Iron Maidens - Marak the Blooded
+ -- Blackhand's Crucible
+ [77325] = true, -- Blackhand
+
+ -------------------------------------------------------------------------------
+ -- Blackrock Mountain: Blackrock Caverns
+ -------------------------------------------------------------------------------
+ [39665] = true, -- Rom'ogg Bonecrusher
+ [39679] = true, -- Corla, Herald of Twilight
+ [39698] = true, -- Karsh Steelbender
+ [39700] = true, -- Beauty
+ [39705] = true, -- Ascendant Lord Obsidius
+
+ -------------------------------------------------------------------------------
+ -- Blackrock Mountain: Blackwing Descent
+ -------------------------------------------------------------------------------
+ [41570] = true, -- Magmaw
+ [42166] = true, -- Arcanotron
+ [42178] = true, -- Magmatron
+ [42179] = true, -- Electron
+ [42180] = true, -- Toxitron
+ [41378] = true, -- Maloriak
+ [41442] = true, -- Atramedes
+ [43296] = true, -- Chimaeron
+ [41376] = true, -- Nefarian
+
+ -------------------------------------------------------------------------------
+ -- Blackwing Lair
+ -------------------------------------------------------------------------------
+ [12435] = true, -- Razorgore the Untamed
+ [13020] = true, -- Vaelastrasz the Corrupt
+ [12017] = true, -- Broodlord Lashlayer
+ [11983] = true, -- Firemaw
+ [14601] = true, -- Ebonroc
+ [11981] = true, -- Flamegor
+ [14020] = true, -- Chromaggus
+ [11583] = true, -- Nefarian
+ [12557] = true, -- Grethok the Controller
+ [10162] = true, -- Lord Victor Nefarius (Also found in Blackrock Spire)
+
+ -------------------------------------------------------------------------------
+ -- Bloodmaul Slag Mines
+ -------------------------------------------------------------------------------
+ [74787] = true, -- Slave Watcher Crushto
+ [74366] = true, -- Forgemaster Gog'duh
+ [75786] = true, -- Roltall
+ [74790] = true, -- Gug'rokk
+
+ -------------------------------------------------------------------------------
+ -- Caverns of Time: Battle for Mount Hyjal
+ -------------------------------------------------------------------------------
+ [17767] = true, -- Rage Winterchill
+ [17808] = true, -- Anetheron
+ [17888] = true, -- Kaz'rogal
+ [17842] = true, -- Azgalor
+ [17968] = true, -- Archimonde
+
+ -------------------------------------------------------------------------------
+ -- Caverns of Time: Culling of Stratholme
+ -------------------------------------------------------------------------------
+ [26529] = true, -- Meathook
+ [26530] = true, -- Salramm the Fleshcrafter
+ [26532] = true, -- Chrono-Lord Epoch
+ [32273] = true, -- Infinite Corruptor
+ [26533] = true, -- Mal'Ganis
+ [29620] = true, -- Mal'Ganis
+
+ -------------------------------------------------------------------------------
+ -- Caverns of Time: Escape from Durnholde Keep
+ -------------------------------------------------------------------------------
+ [17848] = true, -- Lieutenant Drake
+ [17862] = true, -- Captain Skarloc
+ [18096] = true, -- Epoch Hunter
+ [28132] = true, -- Don Carlos
+
+ -------------------------------------------------------------------------------
+ -- Caverns of Time: Opening the Dark Portal
+ -------------------------------------------------------------------------------
+ [17879] = true, -- Chrono Lord Deja
+ [17880] = true, -- Temporus
+ [17881] = true, -- Aeonus
+
+ -------------------------------------------------------------------------------
+ -- Coilfang Reservoir: Serpentshrine Cavern
+ -------------------------------------------------------------------------------
+ [21216] = true, -- Hydross the Unstable
+ [21217] = true, -- The Lurker Below
+ [21215] = true, -- Leotheras the Blind
+ [21214] = true, -- Fathom-Lord Karathress
+ [21213] = true, -- Morogrim Tidewalker
+ [21212] = true, -- Lady Vashj
+ [21875] = true, -- Shadow of Leotheras
+
+ -------------------------------------------------------------------------------
+ -- Coilfang Reservoir: Slave Pens
+ -------------------------------------------------------------------------------
+ [25740] = true, -- Ahune
+ [17941] = true, -- Mennu the Betrayer
+ [17991] = true, -- Rokmar the Crackler
+ [17942] = true, -- Quagmirran
+
+ -------------------------------------------------------------------------------
+ -- Coilfang Reservoir: The Steamvault
+ -------------------------------------------------------------------------------
+ [17797] = true, -- Hydromancer Thespia
+ [17796] = true, -- Mekgineer Steamrigger
+ [17798] = true, -- Warlord Kalithresh
+
+ -------------------------------------------------------------------------------
+ -- Coilfang Reservoir: The Underbog
+ -------------------------------------------------------------------------------
+ [17770] = true, -- Hungarfen
+ [18105] = true, -- Ghaz'an
+ [17826] = true, -- Swamplord Musel'ek
+ [17827] = true, -- Claw
+ [17882] = true, -- The Black Stalker
+
+ -------------------------------------------------------------------------------
+ -- Dire Maul: Arena
+ -------------------------------------------------------------------------------
+ [11447] = true, -- Mushgog
+ [11498] = true, -- Skarr the Unbreakable
+ [11497] = true, -- The Razza
+
+ -------------------------------------------------------------------------------
+ -- Dire Maul: East
+ -------------------------------------------------------------------------------
+ [14354] = true, -- Pusillin
+ [14327] = true, -- Lethtendris
+ [14349] = true, -- Pimgib
+ [13280] = true, -- Hydrospawn
+ [11490] = true, -- Zevrim Thornhoof
+ [11492] = true, -- Alzzin the Wildshaper
+ [16097] = true, -- Isalien
+
+ -------------------------------------------------------------------------------
+ -- Dire Maul: North
+ -------------------------------------------------------------------------------
+ [14326] = true, -- Guard Mol'dar
+ [14322] = true, -- Stomper Kreeg
+ [14321] = true, -- Guard Fengus
+ [14323] = true, -- Guard Slip'kik
+ [14325] = true, -- Captain Kromcrush
+ [14324] = true, -- Cho'Rush the Observer
+ [11501] = true, -- King Gordok
+
+ -------------------------------------------------------------------------------
+ -- Dire Maul: West
+ -------------------------------------------------------------------------------
+ [11489] = true, -- Tendris Warpwood
+ [11487] = true, -- Magister Kalendris
+ [11467] = true, -- Tsu'zee
+ [11488] = true, -- Illyanna Ravenoak
+ [14690] = true, -- Revanchion (Scourge Invasion)
+ [11496] = true, -- Immol'thar
+ [14506] = true, -- Lord Hel'nurath
+ [11486] = true, -- Prince Tortheldrin
+
+ -------------------------------------------------------------------------------
+ -- Drak'Tharon Keep
+ -------------------------------------------------------------------------------
+ [26630] = true, -- Trollgore
+ [26631] = true, -- Novos the Summoner
+ [27483] = true, -- King Dred
+ [26632] = true, -- The Prophet Tharon'ja
+ [27696] = true, -- The Prophet Tharon'ja
+
+ -------------------------------------------------------------------------------
+ -- End Time 4.3
+ -------------------------------------------------------------------------------
+ [54431] = true, -- Echo of Baine
+ [54445] = true, -- Echo of Jaina
+ [54123] = true, -- Echo of Sylvanas
+ [54544] = true, -- Echo of Tyrande
+ [54432] = true, -- Murozond
+
+ -------------------------------------------------------------------------------
+ -- Firelands 4.2 PTR
+ -------------------------------------------------------------------------------
+ [52530] = true, -- Alysrazor
+ [53494] = true, -- Baleroc
+ [52498] = true, -- Bethtilac
+ [52571] = true, -- FandralStaghelm
+ [52409] = true, -- Ragnaros
+ [52558] = true, -- Rhyolith
+ [53691] = true, -- Shannox
+
+ -------------------------------------------------------------------------------
+ -- Forge of Souls
+ -------------------------------------------------------------------------------
+ [36497] = true, -- Bronjahm
+ [36502] = true, -- Devourer of Souls
+
+ -------------------------------------------------------------------------------
+ -- Gate of the Setting Sun (MoP-Dungeon 5.0.1)
+ -------------------------------------------------------------------------------
+ [56589] = true, -- Gadok
+ [56636] = true, -- Rimok
+ [56877] = true, -- Raigon
+ [56906] = true, -- Kiptilak
+
+ -------------------------------------------------------------------------------
+ -- Gnomeregan
+ -------------------------------------------------------------------------------
+ [7800] = true, -- Mekgineer Thermaplugg
+ [7079] = true, -- Viscous Fallout
+ [7361] = true, -- Grubbis
+ [6235] = true, -- Electrocutioner 6000
+ [6229] = true, -- Crowd Pummeler 9-60
+ [6228] = true, -- Dark Iron Ambassador
+ [6231] = true, -- Techbot, outside
+
+ -------------------------------------------------------------------------------
+ -- Gorgrond
+ -------------------------------------------------------------------------------
+ [81252] = true, -- Drov the Ruiner
+ [81535] = true, -- Tarlna the Ageless
+
+ -------------------------------------------------------------------------------
+ -- Grim Batol
+ -------------------------------------------------------------------------------
+ [39625] = true, -- General Umbriss
+ [40177] = true, -- Forgemaster Throngus
+ [40319] = true, -- Drahga Shadowburner
+ [40484] = true, -- Erudax
+
+ -------------------------------------------------------------------------------
+ -- Grimrail Depot
+ -------------------------------------------------------------------------------
+ [77803] = true, -- Railmaster Rocketspark
+ [77816] = true, -- Borka the Brute
+ [79545] = true, -- Nitrogg Thundertower
+ [80005] = true, -- Skylord Tovra
+
+ -------------------------------------------------------------------------------
+ -- Gruul's Lair
+ -------------------------------------------------------------------------------
+ [18831] = true, -- High King Maulgar
+ [19044] = true, -- Gruul the Dragonkiller
+
+ -- Gruul's Lair: Maulgar's Ogre Council
+ [18835] = true, -- Kiggler the Crazed
+ [18836] = true, -- Blindeye the Seer
+ [18834] = true, -- Olm the Summoner
+ [18832] = true, -- Krosh Firehand
+
+ -------------------------------------------------------------------------------
+ -- Gundrak
+ -------------------------------------------------------------------------------
+ [29304] = true, -- Slad'ran
+ [29305] = true, -- Moorabi
+ [29307] = true, -- Drakkari Colossus
+ [29306] = true, -- Gal'darah
+ [29932] = true, -- Eck the Ferocious (Heroic)
+
+ -------------------------------------------------------------------------------
+ -- Halls of Lightning
+ -------------------------------------------------------------------------------
+ [28586] = true, -- General Bjarngrim
+ [28587] = true, -- Volkhan
+ [28546] = true, -- Ionar
+ [28923] = true, -- Loken
+
+ -------------------------------------------------------------------------------
+ -- Halls of Origination
+ -------------------------------------------------------------------------------
+ [39425] = true, -- Temple Guardian Anhuur
+ [39428] = true, -- Earthrager Ptah
+ [39788] = true, -- Anraphet
+ [39587] = true, -- Isiset
+ [39731] = true, -- Ammunae
+ [39732] = true, -- Setesh
+ [39378] = true, -- Rajh
+
+ -------------------------------------------------------------------------------
+ -- Halls of Reflection
+ -------------------------------------------------------------------------------
+ [38112] = true, -- Falric
+ [38113] = true, -- Marwyn
+ [37226] = true, -- The Lich King
+
+ -------------------------------------------------------------------------------
+ -- Halls of Stone
+ -------------------------------------------------------------------------------
+ [27977] = true, -- Krystallus
+ [27975] = true, -- Maiden of Grief
+ [28234] = true, -- The Tribunal of Ages
+ [27978] = true, -- Sjonnir The Ironshaper
+
+ -------------------------------------------------------------------------------
+ -- Heart of Fear (MoP-Raid 5.0.1)
+ -------------------------------------------------------------------------------
+ [62837] = true, -- Grand Empress Shek'zeer
+ [62543] = true, -- Blade Lord Ta'yak
+ [62511] = true, -- Amber-Shaper Un'sok
+ [63191] = true, -- Garalon
+ [62397] = true, -- Wind Lord Mel'jarak
+ [62980] = true, -- Zorlok
+
+ -------------------------------------------------------------------------------
+ -- Hellfire Citadel (Draenor)
+ -------------------------------------------------------------------------------
+ -- Hellbreach
+ [95068] = true, -- Hellfire Assault - Siegemaster Mar'tak
+ [90284] = true, -- Iron Reaver
+ [90776] = true, -- Kormrok
+ -- Halls of Blood
+ [90378] = true, -- Kilrogg Deadeye
+ [92144] = true, -- Hellfire High Council - Dia Darkwhisper
+ [92146] = true, -- Hellfire High Council - Gurtogg Bloodboil
+ [93713] = true, -- Hellfire High Council - Blademaster Jubei'thos
+ [91809] = true, -- Gorefiend
+ -- Bastion of Shadows
+ [95067] = true, -- Shadow-Lord Iskar
+ [90296] = true, -- Socrethar the Eternal - Soulbound Construct
+ [93439] = true, -- Tyrant Velhari
+ -- Destructor�s Rise
+ [89890] = true, -- Fel Lord Zakuun
+ [93068] = true, -- Xhul'horac
+ [91349] = true, -- Mannoroth
+ -- The Black Gate
+ [91331] = true, -- Archimonde
+
+ -------------------------------------------------------------------------------
+ -- Hellfire Citadel: Hellfire Ramparts
+ -------------------------------------------------------------------------------
+ [17306] = true, -- Watchkeeper Gargolmar
+ [17308] = true, -- Omor the Unscarred
+ [17537] = true, -- Vazruden
+ [17307] = true, -- Vazruden the Herald
+ [17536] = true, -- Nazan
+
+ -------------------------------------------------------------------------------
+ -- Hellfire Citadel: Magtheridon's Lair
+ -------------------------------------------------------------------------------
+ [17257] = true, -- Magtheridon
+
+ -------------------------------------------------------------------------------
+ -- Hellfire Citadel: Shattered Halls
+ -------------------------------------------------------------------------------
+ [16807] = true, -- Grand Warlock Nethekurse
+ [20923] = true, -- Blood Guard Porung (Heroic)
+ [16809] = true, -- Warbringer O'mrogg
+ [16808] = true, -- Warchief Kargath Bladefist
+
+ -------------------------------------------------------------------------------
+ -- Hellfire Citadel: The Blood Furnace
+ -------------------------------------------------------------------------------
+ [17381] = true, -- The Maker
+ [17380] = true, -- Broggok
+ [17377] = true, -- Keli'dan the Breaker
+
+ -------------------------------------------------------------------------------
+ -- Hellfire Peninsula
+ -------------------------------------------------------------------------------
+ [18728] = true, -- Doom Lord Kazzak
+ [12397] = true, -- Lord Kazzak
+
+ -------------------------------------------------------------------------------
+ -- Highmaul
+ -------------------------------------------------------------------------------
+ -- Walled City
+ [78714] = true, -- Kargath Bladefist
+ [77404] = true, -- The Butcher
+ [78491] = true, -- Brackenspore
+ -- Arcane Sanctum
+ [78948] = true, -- Tectus
+ [78238] = true, -- Twin Ogron - Pol
+ [78237] = true, -- Twin Ogron - Phemos
+ [79015] = true, -- Ko'ragh
+ -- Imperator's Rise
+ [77428] = true, -- Imperator Mar'gok
+
+ -------------------------------------------------------------------------------
+ -- Hour of Twilight 4.3
+ -------------------------------------------------------------------------------
+ [54590] = true, -- Arcurion
+ [54968] = true, -- Asira Dawnslayer
+ [54938] = true, -- Archbishop Benedictus
+
+ -------------------------------------------------------------------------------
+ -- Icecrown Citadel
+ -------------------------------------------------------------------------------
+ [36612] = true, -- Lord Marrowgar
+ [36855] = true, -- Lady Deathwhisper
+
+ -- Gunship Battle
+ [37813] = true, -- Deathbringer Saurfang
+ [36626] = true, -- Festergut
+ [36627] = true, -- Rotface
+ [36678] = true, -- Professor Putricide
+ [37972] = true, -- Prince Keleseth (Icecrown Citadel)
+ [37970] = true, -- Prince Valanar
+ [37973] = true, -- Prince Taldaram (Icecrown Citadel)
+ [37955] = true, -- Queen Lana'thel
+ [36789] = true, -- Valithria Dreamwalker
+ [37950] = true, -- Valithria Dreamwalker (Phased)
+ [37868] = true, -- Risen Archmage, Valitrhia Add
+ [36791] = true, -- Blazing Skeleton, Valithria Add
+ [37934] = true, -- Blistering Zombie, Valithria Add
+ [37886] = true, -- Gluttonous Abomination, Valithria Add
+ [37985] = true, -- Dream Cloud , Valithria "Add"
+ [36853] = true, -- Sindragosa
+ [36597] = true, -- The Lich King (Icecrown Citadel)
+ [37217] = true, -- Precious
+ [37025] = true, -- Stinki
+ [36661] = true, -- Rimefang
+
+ -------------------------------------------------------------------------------
+ -- Iron Docks
+ -------------------------------------------------------------------------------
+ [81305] = true, -- Fleshrender Nok'gar
+ [81297] = true, -- Fleshrender Nok'gar - Dreadfang
+ [80816] = true, -- Grimrail Enforcers - Ahri'ok Dugru
+ [80805] = true, -- Grimrail Enforcers - Makogg Emberblade
+ [80808] = true, -- Grimrail Enforcers - Neesa Nox
+ [79852] = true, -- Oshir
+ [83612] = true, -- Skulloc
+ [83613] = true, -- Skulloc - Koramar
+ [83616] = true, -- Skulloc - Zoggosh
+
+ -------------------------------------------------------------------------------
+ -- Isle of Giants
+ -------------------------------------------------------------------------------
+ [69161] = true, -- Oondasta
+
+ -------------------------------------------------------------------------------
+ -- Isle of Thunder
+ -------------------------------------------------------------------------------
+ [69099] = true, -- Nalak
+
+ -------------------------------------------------------------------------------
+ -- Karazhan
+ -------------------------------------------------------------------------------
+ [15550] = true, -- Attumen the Huntsman
+ [16151] = true, -- Midnight
+ [28194] = true, -- Tenris Mirkblood (Scourge invasion)
+ [15687] = true, -- Moroes
+ [16457] = true, -- Maiden of Virtue
+ [15691] = true, -- The Curator
+ [15688] = true, -- Terestian Illhoof
+ [16524] = true, -- Shade of Aran
+ [15689] = true, -- Netherspite
+ [15690] = true, -- Prince Malchezaar
+ [17225] = true, -- Nightbane
+ [17229] = true, -- Kil'rek
+ -- Chess event
+
+ -------------------------------------------------------------------------------
+ -- Karazhan: Servants' Quarters Beasts
+ -------------------------------------------------------------------------------
+ [16179] = true, -- Hyakiss the Lurker
+ [16181] = true, -- Rokad the Ravager
+ [16180] = true, -- Shadikith the Glider
+
+ -------------------------------------------------------------------------------
+ -- Karazhan: Opera Event
+ -------------------------------------------------------------------------------
+ [17535] = true, -- Dorothee
+ [17546] = true, -- Roar
+ [17543] = true, -- Strawman
+ [17547] = true, -- Tinhead
+ [17548] = true, -- Tito
+ [18168] = true, -- The Crone
+ [17521] = true, -- The Big Bad Wolf
+ [17533] = true, -- Romulo
+ [17534] = true, -- Julianne
+
+ -------------------------------------------------------------------------------
+ -- Kun-Lai Summit: MoP World bosses
+ -------------------------------------------------------------------------------
+ [60491] = true, -- Sha of Anger
+ [62346] = true, -- Galleon
+
+ -------------------------------------------------------------------------------
+ -- Lost City of the Tol'vir
+ -------------------------------------------------------------------------------
+ [44577] = true, -- General Husam
+ [43612] = true, -- High Prophet Barim
+ [43614] = true, -- Lockmaw
+ [49045] = true, -- Augh
+ [44819] = true, -- Siamat
+
+ -------------------------------------------------------------------------------
+ -- Lower Blackrock Spire
+ -------------------------------------------------------------------------------
+ [10263] = true, -- Burning Felguard
+ [9218] = true, -- Spirestone Battle Lord
+ [9219] = true, -- Spirestone Butcher
+ [9217] = true, -- Spirestone Lord Magus
+ [9196] = true, -- Highlord Omokk
+ [9236] = true, -- Shadow Hunter Vosh'gajin
+ [9237] = true, -- War Master Voone
+ [16080] = true, -- Mor Grayhoof
+ [9596] = true, -- Bannok Grimaxe
+ [10596] = true, -- Mother Smolderweb
+ [10376] = true, -- Crystal Fang
+ [10584] = true, -- Urok Doomhowl
+ [9736] = true, -- Quartermaster Zigris
+ [10220] = true, -- Halycon
+ [10268] = true, -- Gizrul the Slavener
+ [9718] = true, -- Ghok Bashguud
+ [9568] = true, -- Overlord Wyrmthalak
+
+ -------------------------------------------------------------------------------
+ -- Magisters' Terrace
+ -------------------------------------------------------------------------------
+ [24723] = true, -- Selin Fireheart
+ [24744] = true, -- Vexallus
+ [24560] = true, -- Priestess Delrissa
+ [24664] = true, -- Kael'thas Sunstrider
+
+ -------------------------------------------------------------------------------
+ -- Maraudon
+ -------------------------------------------------------------------------------
+ -- [13718] = true, -- The Nameless Prophet (Pre-instance)
+ [13742] = true, -- Kolk
+ [13741] = true, -- Gelk
+ [13740] = true, -- Magra
+ [13739] = true, -- Maraudos
+ [12236] = true, -- Lord Vyletongue
+ [13738] = true, -- Veng
+ [13282] = true, -- Noxxion
+ [12258] = true, -- Razorlash
+ [12237] = true, -- Meshlok the Harvester
+ [12225] = true, -- Celebras the Cursed
+ [12203] = true, -- Landslide
+ [13601] = true, -- Tinkerer Gizlock
+ [13596] = true, -- Rotgrip
+ [12201] = true, -- Princess Theradras
+
+ -------------------------------------------------------------------------------
+ -- Mogu'Shan Palace (MoP-Dungeon 5.0.1)
+ -------------------------------------------------------------------------------
+ [61442] = true, -- Kuai the Brute, Trial of the King
+ [61444] = true, -- Ming the Cunning, Trial of the King
+ [61445] = true, -- Haiyan the Unstoppable, Trial of the King
+ [61243] = true, -- Gekkan
+ [61398] = true, -- Xin the Weaponmaster
+
+ -------------------------------------------------------------------------------
+ -- Mogu'Shan Vault (MoP-Raid 5.0.1)
+ -------------------------------------------------------------------------------
+ [59915] = true, -- Jasper, Stone Guard
+ [60009] = true, -- Feng the Accursed
+ [60043] = true, -- Jade, Stone Guard
+ [60047] = true, -- Amethyst, Stone Guard
+ [60051] = true, -- Cobalt, Stone Guard
+ [60143] = true, -- Gara'jal the Spiritbinder
+ [60399] = true, -- Qin-xi
+ [60400] = true, -- Jan-xi
+ [60410] = true, -- Elegon
+ [60701] = true, -- Zian of the Endless Shadow
+ [60709] = true, -- Qiang the Merciless
+ [60710] = true, -- Subetai the Swift
+ [60708] = true, -- Meng the Demented
+
+ -------------------------------------------------------------------------------
+ -- Molten Core
+ -------------------------------------------------------------------------------
+ [12118] = true, -- Lucifron
+ [11982] = true, -- Magmadar
+ [12259] = true, -- Gehennas
+ [12057] = true, -- Garr
+ [12056] = true, -- Baron Geddon
+ [12264] = true, -- Shazzrah
+ [12098] = true, -- Sulfuron Harbinger
+ [11988] = true, -- Golemagg the Incinerator
+ [12018] = true, -- Majordomo Executus
+ [11502] = true, -- Ragnaros
+
+ -------------------------------------------------------------------------------
+ -- Nagrand
+ -------------------------------------------------------------------------------
+ [18398] = true, -- Brokentoe
+ [18069] = true, -- Mogor , friendly
+ [18399] = true, -- Murkblood Twin
+ [18400] = true, -- Rokdar the Sundered Lord
+ [18401] = true, -- Skra'gath
+ [18402] = true, -- Warmaul Champion
+
+ -------------------------------------------------------------------------------
+ -- Naxxramas
+ -------------------------------------------------------------------------------
+ [30549] = true, -- Baron Rivendare (Naxxramas)
+ [16803] = true, -- Death Knight Understudy
+ [15930] = true, -- Feugen
+ [15929] = true, -- Stalagg
+
+ -------------------------------------------------------------------------------
+ -- Naxxramas: Abomination Wing
+ -------------------------------------------------------------------------------
+ [16028] = true, -- Patchwerk
+ [15931] = true, -- Grobbulus
+ [15932] = true, -- Gluth
+ [15928] = true, -- Thaddius
+
+ -------------------------------------------------------------------------------
+ -- Naxxramas: Deathknight Wing
+ -------------------------------------------------------------------------------
+ [16061] = true, -- Instructor Razuvious
+ [16060] = true, -- Gothik the Harvester
+
+ -------------------------------------------------------------------------------
+ -- Naxxramas: Frostwyrm Lair
+ -------------------------------------------------------------------------------
+ [15989] = true, -- Sapphiron
+ [15990] = true, -- Kel'Thuzad
+ [25465] = true, -- Kel'Thuzad
+
+ -------------------------------------------------------------------------------
+ -- Naxxramas: Plague Wing
+ -------------------------------------------------------------------------------
+ [15954] = true, -- Noth the Plaguebringer
+ [15936] = true, -- Heigan the Unclean
+ [16011] = true, -- Loatheb
+
+ -------------------------------------------------------------------------------
+ -- Naxxramas: Spider Wing
+ -------------------------------------------------------------------------------
+ [15956] = true, -- Anub'Rekhan
+ [15953] = true, -- Grand Widow Faerlina
+ [15952] = true, -- Maexxna
+
+ -------------------------------------------------------------------------------
+ -- Naxxramas: The Four Horsemen
+ -------------------------------------------------------------------------------
+ [16065] = true, -- Lady Blaumeux
+ [16064] = true, -- Thane Korth'azz
+ [16062] = true, -- Highlord Mograine
+ [16063] = true, -- Sir Zeliek
+
+ -------------------------------------------------------------------------------
+ -- Nizuao Temple (MoP-Dungeon 5.0.1)
+ -------------------------------------------------------------------------------
+
+ [61634] = true, -- Vojak
+ [61567] = true, -- Jinbak
+ [62205] = true, -- Neronok
+ [61485] = true, -- Pavalak
+
+ -------------------------------------------------------------------------------
+ -- Obsidian Sanctum
+ -------------------------------------------------------------------------------
+ [30451] = true, -- Shadron
+ [30452] = true, -- Tenebron
+ [30449] = true, -- Vesperon
+ [28860] = true, -- Sartharion
+
+ -------------------------------------------------------------------------------
+ -- Onyxia's Lair
+ -------------------------------------------------------------------------------
+ [10184] = true, -- Onyxia
+
+ -------------------------------------------------------------------------------
+ -- Pit of Saron
+ -------------------------------------------------------------------------------
+ [36494] = true, -- Forgemaster Garfrost
+ [36477] = true, -- Krick
+ [36476] = true, -- Ick
+ [36658] = true, -- Scourgelord Tyrannus
+
+ -------------------------------------------------------------------------------
+ -- Ragefire Chasm
+ -------------------------------------------------------------------------------
+ [11517] = true, -- Oggleflint
+ [11518] = true, -- Jergosh the Invoker
+ [11519] = true, -- Bazzalan
+ [11520] = true, -- Taragaman the Hungerer
+ [17830] = true, -- Zelemar the Wrathful
+
+ -------------------------------------------------------------------------------
+ -- Razorfen Downs
+ -------------------------------------------------------------------------------
+ [7355] = true, -- Tuten'kash
+ [14686] = true, -- Lady Falther'ess (Scourge invasion only)
+ [7356] = true, -- Plaguemaw the Rotting
+ [7357] = true, -- Mordresh Fire Eye
+ [8567] = true, -- Glutton
+ [7354] = true, -- Ragglesnout
+ [7358] = true, -- Amnennar the Coldbringer
+
+ -------------------------------------------------------------------------------
+ -- Razorfen Kraul
+ -------------------------------------------------------------------------------
+ [4421] = true, -- Charlga Razorflank
+ [4420] = true, -- Overlord Ramtusk
+ [4422] = true, -- Agathelos the Raging
+ [4428] = true, -- Death Speaker Jargba
+ [4424] = true, -- Aggem Thorncurse
+ [6168] = true, -- Roogug
+ [4425] = true, -- Blind Hunter
+ [4842] = true, -- Earthcaller Halmgar
+
+ -------------------------------------------------------------------------------
+ -- Ruby Sanctum
+ -------------------------------------------------------------------------------
+ [39746] = true, -- Zarithrian
+ [39747] = true, -- Saviana
+ [39751] = true, -- Baltharus
+ [39863] = true, -- Halion
+ [39899] = true, -- Baltharus (Copy has an own id apparently)
+ [40142] = true, -- Halion (twilight realm)
+
+ -------------------------------------------------------------------------------
+ -- Ruins of Ahn'Qiraj
+ -------------------------------------------------------------------------------
+ [15348] = true, -- Kurinnaxx
+ [15341] = true, -- General Rajaxx
+ [15340] = true, -- Moam
+ [15370] = true, -- Buru the Gorger
+ [15369] = true, -- Ayamiss the Hunter
+ [15339] = true, -- Ossirian the Unscarred
+
+ -------------------------------------------------------------------------------
+ -- Scarlet Halls (MoP Dungeon)
+ -------------------------------------------------------------------------------
+ [58632] = true, -- Armsmaster Harlan
+ [59150] = true, -- Flameweaver Koegler
+ [59303] = true, -- Houndmaster Braun
+
+ -------------------------------------------------------------------------------
+ -- Scarlet Monastery: Armory
+ -------------------------------------------------------------------------------
+ [3975] = true, -- Herod
+
+ -------------------------------------------------------------------------------
+ -- Scarlet Monastery: Cathedral
+ -------------------------------------------------------------------------------
+ [4542] = true, -- High Inquisitor Fairbanks
+ [3976] = true, -- Scarlet Commander Mograine
+ [3977] = true, -- High Inquisitor Whitemane
+ [59789] = true, -- Thalnos the Soulrender (MoP Heroic 5.0.1)
+ [59223] = true, -- Brother Korlof (MoP Heroic 5.0.1)
+ [60040] = true, -- Commander Durand (MoP Heroic 5.0.1)
+
+ -------------------------------------------------------------------------------
+ -- Scarlet Monastery: Graveyard
+ -------------------------------------------------------------------------------
+ [3983] = true, -- Interrogator Vishas
+ [6488] = true, -- Fallen Champion
+ [6490] = true, -- Azshir the Sleepless
+ [6489] = true, -- Ironspine
+ [14693] = true, -- Scorn (Scourge invasion only)
+ [4543] = true, -- Bloodmage Thalnos
+ [23682] = true, -- Headless Horseman
+ [23800] = true, -- Headless Horseman
+
+ -------------------------------------------------------------------------------
+ -- Scarlet Monastery: Library
+ -------------------------------------------------------------------------------
+ [3974] = true, -- Houndmaster Loksey
+ [6487] = true, -- Arcanist Doan
+
+ -------------------------------------------------------------------------------
+ -- Scholomance
+ -------------------------------------------------------------------------------
+ [14861] = true, -- Blood Steward of Kirtonos
+ [10506] = true, -- Kirtonos the Herald
+ [14695] = true, -- Lord Blackwood (Scourge Invasion)
+ [10503] = true, -- Jandice Barov
+ [11622] = true, -- Rattlegore
+ [14516] = true, -- Death Knight Darkreaver
+ [10433] = true, -- Marduk Blackpool
+ [10432] = true, -- Vectus
+ [16118] = true, -- Kormok
+ [10508] = true, -- Ras Frostwhisper
+ [10505] = true, -- Instructor Malicia
+ [11261] = true, -- Doctor Theolen Krastinov
+ [10901] = true, -- Lorekeeper Polkelt
+ [10507] = true, -- The Ravenian
+ [10504] = true, -- Lord Alexei Barov
+ [10502] = true, -- Lady Illucia Barov
+ [1853] = true, -- Darkmaster Gandling
+ [58633] = true, -- Instructor Chillheart (MoP Heroic)
+ [59200] = true, -- Lilian Voss (MoP Heroic)
+
+ -------------------------------------------------------------------------------
+ -- Searing Gorge
+ -------------------------------------------------------------------------------
+ [9026] = true, -- Overmaster Pyron
+
+ -------------------------------------------------------------------------------
+ -- Shado-Pan Monastery (MoP Dungeon)
+ -------------------------------------------------------------------------------
+ [56719] = true, -- Sha of Violence
+ [56747] = true, -- Gu Cloudstrike
+ [56884] = true, -- Taran Zhu
+ [64387] = true, -- Master Snowdrift
+
+ -------------------------------------------------------------------------------
+ -- Shadowfang Keep
+ -------------------------------------------------------------------------------
+ [3914] = true, -- Rethilgore
+ [3886] = true, -- Razorclaw the Butcher
+ [4279] = true, -- Odo the Blindwatcher
+ [3887] = true, -- Baron Silverlaine
+ [4278] = true, -- Commander Springvale
+ [4274] = true, -- Fenrus the Devourer
+ [3927] = true, -- Wolf Master Nandos
+ [14682] = true, -- Sever (Scourge invasion only)
+ [4275] = true, -- Archmage Arugal
+ [3872] = true, -- Deathsworn Captain
+ [46962] = true, -- Baron Ashbury
+ [46963] = true, -- Lord Walden
+ [46964] = true, -- Lord Godfrey
+
+ -------------------------------------------------------------------------------
+ -- Shadowmoon Burial Grounds
+ -------------------------------------------------------------------------------
+ [75509] = true, -- Sadana Bloodfury
+ [75829] = true, -- Nhallish
+ [75452] = true, -- Bonemaw
+ [76407] = true, -- Ner'zhul
+
+ -------------------------------------------------------------------------------
+ -- Shadowmoon Valley
+ -------------------------------------------------------------------------------
+ [17711] = true, -- Doomwalker
+
+ -------------------------------------------------------------------------------
+ -- Siege of Orgrimmar
+ -------------------------------------------------------------------------------
+ -- Vale of Eternal Sorrows
+ [71543] = true, -- Immerseus
+ [71475] = true, -- Rook Stonetoe, The Fallen Protectors
+ [71479] = true, -- He Softfoot, The Fallen Protectors
+ [71480] = true, -- Sun Tenderheart, The Fallen Protectors
+ [72276] = true, -- Norushen, Amalgam of Corruption
+ [71734] = true, -- Sha of Pride
+
+ -- Gates of Retribution
+ [72249] = true, -- Galakras
+ [72311] = true, -- Varian (part of the Galakras encounter's trigger)
+ [72560] = true, -- Lor'Themar (His hair triggers the Galakras encounter)
+ [71466] = true, -- Iron Juggernaut
+ [71859] = true, -- Haromm, his Darkness exceeded only by his Shamanism
+ [71858] = true, -- Kardriss, his Shamanism exceeded only by his Darkness
+ [71515] = true, -- General Nazgrim
+
+ -- The Underhold
+ [71454] = true, -- Malkorak
+ [73720] = true, -- Mogu Spoils (Spoils of War)
+ [71512] = true, -- Mantid Spoils (Spoils of War)
+ [71529] = true, -- Thok the Bloodthirsty, her Thirst exceeded only by her Blood
+
+ -- Downfall
+ [71504] = true, -- Siegecrafter Blackfuse, his fuses exceeded only by his... wait...
+ [71591] = true, -- Automated Shredder (Part of the Siegecrafter fight... not sure if this is the trigger or just him)
+ [71152] = true, -- Skeer the Bloodseeker,
+ [71153] = true, -- Hisek the Swarmkeeper,
+ [71154] = true, -- Ka'roz the Locust,
+ [71155] = true, -- Korven the Prime,
+ [71156] = true, -- Kaz'tik the Manipulator,
+ [71157] = true, -- Xaril The POisoned Mind,
+ [71158] = true, -- Rik'kal the Dissector,
+ [71160] = true, -- Iyyokuk the Lucid, (71159 is Ghazrooki, weird isn't it?)
+ [71161] = true, -- Kil'ruk the Wind-Reaver,
+ [71865] = true, -- Garrosh Starscream, That's right. He was a Decepticon all this time
+
+ -------------------------------------------------------------------------------
+ -- Silithus
+ -------------------------------------------------------------------------------
+ [15205] = true, -- Baron Kazum
+ [15204] = true, -- High Marshal Whirlaxis
+ [15305] = true, -- Lord Skwol
+ [15203] = true, -- Prince Skaldrenox
+ [14454] = true, -- The Windreaver
+
+ -------------------------------------------------------------------------------
+ -- Skyreach
+ -------------------------------------------------------------------------------
+ [75964] = true, -- Ranjit
+ [76141] = true, -- Araknath
+ [76379] = true, -- Rukhran
+ [76266] = true, -- High Sage Viryx
+
+ -------------------------------------------------------------------------------
+ -- Spires of Arak
+ -------------------------------------------------------------------------------
+ [83746] = true, -- Rukhmar
+
+ -------------------------------------------------------------------------------
+ -- Stormstout Brewery (MoP Dungeon)
+ -------------------------------------------------------------------------------
+ [56717] = true, -- Hoptallus
+ [57963] = true, -- Ook-Ook
+ [59479] = true, -- Yan-Zhu the Unsacked
+
+ -------------------------------------------------------------------------------
+ -- Stormwind Stockade
+ -------------------------------------------------------------------------------
+ [1716] = true, -- Bazil Thredd
+ [1663] = true, -- Dextren Ward
+ [1717] = true, -- Hamhock
+ [1666] = true, -- Kam Deepfury
+ [1696] = true, -- Targorr the Dread
+ [1720] = true, -- Bruegal Ironknuckle
+ -- Cata:
+ [46383] = true, -- Randolph Moloch
+ [46264] = true, -- Lord Overheat
+ [46254] = true, -- Hogger
+
+ -------------------------------------------------------------------------------
+ -- Stratholme: Scarlet Stratholme
+ -------------------------------------------------------------------------------
+ [10393] = true, -- Skul
+ [14684] = true, -- Balzaphon (Scourge Invasion)
+ -- [11082] = true, -- Stratholme Courier
+ [11058] = true, -- Fras Siabi
+ [10558] = true, -- Hearthsinger Forresten
+ [10516] = true, -- The Unforgiven
+ [16387] = true, -- Atiesh
+ [11143] = true, -- Postmaster Malown
+ [10808] = true, -- Timmy the Cruel
+ [11032] = true, -- Malor the Zealous
+ [11120] = true, -- Crimson Hammersmith
+ [10997] = true, -- Cannon Master Willey
+ [10811] = true, -- Archivist Galford
+ [10813] = true, -- Balnazzar
+ [16101] = true, -- Jarien
+ [16102] = true, -- Sothos
+
+ -------------------------------------------------------------------------------
+ -- Stratholme: Defenders of the Chapel
+ -------------------------------------------------------------------------------
+ [17913] = true, -- Aelmar the Vanquisher
+ [17911] = true, -- Cathela the Seeker
+ [17910] = true, -- Gregor the Justiciar
+ [17914] = true, -- Vicar Hieronymus
+ [17912] = true, -- Nemas the Arbiter
+
+ -------------------------------------------------------------------------------
+ -- Stratholme: Undead Stratholme
+ -------------------------------------------------------------------------------
+ [10809] = true, -- Stonespine
+ [10437] = true, -- Nerub'enkan
+ [10436] = true, -- Baroness Anastari
+ [11121] = true, -- Black Guard Swordsmith
+ [10438] = true, -- Maleki the Pallid
+ [10435] = true, -- Magistrate Barthilas
+ [10439] = true, -- Ramstein the Gorger
+ [10440] = true, -- Baron Rivendare (Stratholme)
+
+ -------------------------------------------------------------------------------
+ -- Sunwell Plateau
+ -------------------------------------------------------------------------------
+ [24891] = true, -- Kalecgos
+ [25319] = true, -- Kalecgos
+ [24850] = true, -- Kalecgos
+ [24882] = true, -- Brutallus
+ [25038] = true, -- Felmyst
+ [25165] = true, -- Lady Sacrolash
+ [25166] = true, -- Grand Warlock Alythess
+ [25741] = true, -- M'uru
+ [25315] = true, -- Kil'jaeden
+ [25840] = true, -- Entropius
+ [24892] = true, -- Sathrovarr the Corruptor
+
+ -------------------------------------------------------------------------------
+ -- Tanaan Jungle
+ -------------------------------------------------------------------------------
+ [94015] = true, -- Supreme Lord Kazzak
+
+ -------------------------------------------------------------------------------
+ -- Tempest Keep: The Arcatraz
+ -------------------------------------------------------------------------------
+ [20870] = true, -- Zereketh the Unbound
+ [20886] = true, -- Wrath-Scryer Soccothrates
+ [20885] = true, -- Dalliah the Doomsayer
+ [20912] = true, -- Harbinger Skyriss
+ [20904] = true, -- Warden Mellichar
+
+ -------------------------------------------------------------------------------
+ -- Tempest Keep: The Botanica
+ -------------------------------------------------------------------------------
+ [17976] = true, -- Commander Sarannis
+ [17975] = true, -- High Botanist Freywinn
+ [17978] = true, -- Thorngrin the Tender
+ [17980] = true, -- Laj
+ [17977] = true, -- Warp Splinter
+
+ -------------------------------------------------------------------------------
+ -- Tempest Keep: The Eye
+ -------------------------------------------------------------------------------
+ [19514] = true, -- Al'ar
+ [19516] = true, -- Void Reaver
+ [18805] = true, -- High Astromancer Solarian
+ [19622] = true, -- Kael'thas Sunstrider
+ [20064] = true, -- Thaladred the Darkener
+ [20060] = true, -- Lord Sanguinar
+ [20062] = true, -- Grand Astromancer Capernian
+ [20063] = true, -- Master Engineer Telonicus
+ [21270] = true, -- Cosmic Infuser
+ [21269] = true, -- Devastation
+ [21271] = true, -- Infinity Blades
+ [21268] = true, -- Netherstrand Longbow
+ [21273] = true, -- Phaseshift Bulwark
+ [21274] = true, -- Staff of Disintegration
+ [21272] = true, -- Warp Slicer
+
+ -------------------------------------------------------------------------------
+ -- Tempest Keep: The Mechanar
+ -------------------------------------------------------------------------------
+ [19218] = true, -- Gatewatcher Gyro-Kill
+ [19710] = true, -- Gatewatcher Iron-Hand
+ [19219] = true, -- Mechano-Lord Capacitus
+ [19221] = true, -- Nethermancer Sepethrea
+ [19220] = true, -- Pathaleon the Calculator
+
+ -------------------------------------------------------------------------------
+ -- Temple of Ahn'Qiraj
+ -------------------------------------------------------------------------------
+ [15263] = true, -- The Prophet Skeram
+ [15511] = true, -- Lord Kri
+ [15543] = true, -- Princess Yauj
+ [15544] = true, -- Vem
+ [15516] = true, -- Battleguard Sartura
+ [15510] = true, -- Fankriss the Unyielding
+ [15299] = true, -- Viscidus
+ [15509] = true, -- Princess Huhuran
+ [15276] = true, -- Emperor Vek'lor
+ [15275] = true, -- Emperor Vek'nilash
+ [15517] = true, -- Ouro
+ [15727] = true, -- C'Thun
+ [15589] = true, -- Eye of C'Thun
+
+ -------------------------------------------------------------------------------
+ -- Temple of Atal'Hakkar
+ -------------------------------------------------------------------------------
+ [1063] = true, -- Jade
+ [5400] = true, -- Zekkis
+ [5713] = true, -- Gasher
+ [5715] = true, -- Hukku
+ [5714] = true, -- Loro
+ [5717] = true, -- Mijan
+ [5712] = true, -- Zolo
+ [5716] = true, -- Zul'Lor
+ [5399] = true, -- Veyzhak the Cannibal
+ [5401] = true, -- Kazkaz the Unholy
+ [8580] = true, -- Atal'alarion
+ [8443] = true, -- Avatar of Hakkar
+ [5711] = true, -- Ogom the Wretched
+ [5710] = true, -- Jammal'an the Prophet
+ [5721] = true, -- Dreamscythe
+ [5720] = true, -- Weaver
+ [5719] = true, -- Morphaz
+ [5722] = true, -- Hazzas
+ [5709] = true, -- Shade of Eranikus
+
+ -------------------------------------------------------------------------------
+ -- Temple of the Jade Serpent (MoP-Dungeon 5.0.1)
+ -------------------------------------------------------------------------------
+ [56448] = true, -- Wise Mari
+ [58826] = true, -- Zao Sunseeker /Library Event
+ [59051] = true, -- (Strife) - Library Event
+ [59726] = true, -- (Anger) - Library Event
+ [56732] = true, -- Liu Flameheart
+ [56439] = true, -- Sha of Doubt
+
+ -------------------------------------------------------------------------------
+ -- Terrace of Endless Spring (MoP Raid)
+ -------------------------------------------------------------------------------
+ [60583] = true, -- Protector Kaolan
+ [60585] = true, -- Elder Regail
+ [60586] = true, -- Elder Asani
+ [60999] = true, -- Sha of Fear
+ [62442] = true, -- Tsulong
+ [63099] = true, -- Lei Shi
+
+ -------------------------------------------------------------------------------
+ -- The Bastion of Twilight
+ -------------------------------------------------------------------------------
+ [45992] = true, -- Valiona
+ [45993] = true, -- Theralion
+ [44600] = true, -- Halfus Wyrmbreaker
+ [43686] = true, -- Ignacious
+ [43687] = true, -- Feludius
+ [43688] = true, -- Arion
+ [43689] = true, -- Terrastra
+ [43735] = true, -- Elementium Monstrosity
+ [43324] = true, -- Cho'gall
+ [45213] = true, -- Sinestra (Heroic) drycoded from http://db.mmo-champion.com/c/45213/sinestra/
+
+ -------------------------------------------------------------------------------
+ -- The Deadmines
+ -------------------------------------------------------------------------------
+ [642] = true, -- Sneed's Shredder
+ [643] = true, -- Sneed
+ [644] = true, -- Rhahk'Zor
+ [645] = true, -- Cookie
+ [646] = true, -- Mr. Smite
+ [647] = true, -- Captain Greenskin
+ [3586] = true, -- Miner Johnson
+ [1763] = true, -- Gilnid
+ [639] = true, -- Edwin VanCleef
+ [596] = true, -- Brainwashed Noble, outside
+ [626] = true, -- Foreman Thistlenettle, outside
+ [599] = true, -- Marisa du'Paige, outside
+ [47162] = true, -- Glubtok
+ [47296] = true, -- Helix Gearbreaker
+ [43778] = true, -- Foe Reaper 5000
+ [47626] = true, -- Admiral Ripsnarl
+ [47739] = true, -- "Captain" Cookie
+ [49541] = true, -- Vanessa VanCleef
+
+ -------------------------------------------------------------------------------
+ -- Dragon Soul (4.3 Raid)
+ -------------------------------------------------------------------------------
+ [55265] = true, -- Morchok
+ [55308] = true, -- Warlord Zonozz
+ [55312] = true, -- Yor'sahj the Unsleeping
+ [55689] = true, -- Hagara the Binder
+ [55294] = true, -- Ultraxion
+ [56427] = true, -- Warmaster Blackhorn
+ [53879] = true, -- Spine Deathwing
+ [56173] = true, -- Madness Deathwing
+
+ -------------------------------------------------------------------------------
+ -- The Everbloom
+ -------------------------------------------------------------------------------
+ [81522] = true, -- Witherbark
+ [83892] = true, -- Ancient Protectors - Life Warden Gola
+ [83893] = true, -- Ancient Protectors - Earthshaper Telu
+ [83894] = true, -- Ancient Protectors - Dulhu
+ [84550] = true, -- Xeri'tac
+ [82682] = true, -- Archmage Sol
+ [83846] = true, -- Yalnu
+
+ -------------------------------------------------------------------------------
+ -- The Eye of Eternity
+ -------------------------------------------------------------------------------
+ [28859] = true, -- Malygos
+
+ -------------------------------------------------------------------------------
+ -- The Nexus
+ -------------------------------------------------------------------------------
+ [26798] = true, -- Commander Kolurg (Heroic)
+ [26796] = true, -- Commander Stoutbeard (Heroic)
+ [26731] = true, -- Grand Magus Telestra
+ [26832] = true, -- Grand Magus Telestra
+ [26928] = true, -- Grand Magus Telestra
+ [26929] = true, -- Grand Magus Telestra
+ [26930] = true, -- Grand Magus Telestra
+ [26763] = true, -- Anomalus
+ [26794] = true, -- Ormorok the Tree-Shaper
+ [26723] = true, -- Keristrasza
+
+ -------------------------------------------------------------------------------
+ -- The Oculus
+ -------------------------------------------------------------------------------
+ [27654] = true, -- Drakos the Interrogator
+ [27447] = true, -- Varos Cloudstrider
+ [27655] = true, -- Mage-Lord Urom
+ [27656] = true, -- Ley-Guardian Eregos
+
+ -------------------------------------------------------------------------------
+ -- The Stonecore
+ -------------------------------------------------------------------------------
+ [43438] = true, -- Corborus
+ [43214] = true, -- Slabhide
+ [42188] = true, -- Ozruk
+ [42333] = true, -- High Priestess Azil
+
+ -------------------------------------------------------------------------------
+ -- The Violet Hold
+ -------------------------------------------------------------------------------
+ [29315] = true, -- Erekem
+ [29313] = true, -- Ichoron
+ [29312] = true, -- Lavanthor
+ [29316] = true, -- Moragg
+ [29266] = true, -- Xevozz
+ [29314] = true, -- Zuramat the Obliterator
+ [31134] = true, -- Cyanigosa
+
+ -------------------------------------------------------------------------------
+ -- The Vortex Pinnacle
+ -------------------------------------------------------------------------------
+ [43878] = true, -- Grand Vizier Ertan
+ [43873] = true, -- Altairus
+ [43875] = true, -- Asaad
+
+ -------------------------------------------------------------------------------
+ -- Throne of the Four Winds
+ -------------------------------------------------------------------------------
+ [45871] = true, -- Nezir
+ [46753] = true, -- Al'Akir
+
+ -------------------------------------------------------------------------------
+ -- Throne of Thunder
+ -------------------------------------------------------------------------------
+ -- Last Stand of the Zandalari
+ [69465] = true, -- Jin'rokh the Breaker
+ [68476] = true, -- Horridon
+ [69134] = true, -- Kazra'jin, Council of Elders
+ [69078] = true, -- Sul the Sandcrawler, Council of Elders
+ [69131] = true, -- Frost King Malakk, Council of Elders
+ [69132] = true, -- High Priestess Mar'li, Council of Elders
+
+ -- Forgotten Depths
+ [67977] = true, -- Tortos
+ [70212] = true, -- Flaming Head
+ [70235] = true, -- Frozen Head
+ [70247] = true, -- Venomous Head
+ [69712] = true, -- Ji-Kun
+
+ -- Halls of Flesh-Shaping
+ [68036] = true, -- Durumu the Forgotten
+ [69017] = true, -- Primordius
+ [69427] = true, -- Dark Animus
+
+ -- Pinnacle of Storms
+ [68078] = true, -- Iron Qon
+ [68905] = true, -- Lu'lin , Twin Consorts
+ [68904] = true, -- Suen , Twin Consorts
+ [68397] = true, -- Lei Shen
+ [69473] = true, -- Ra-den
+
+ -------------------------------------------------------------------------------
+ -- Trial of the Champion
+ -------------------------------------------------------------------------------
+ -- Alliance
+ [35617] = true, -- Deathstalker Visceri
+ [35569] = true, -- Eressea Dawnsinger
+ [35572] = true, -- Mokra the Skullcrusher
+ [35571] = true, -- Runok Wildmane
+ [35570] = true, -- Zul'tore
+
+ -- Horde
+ [34702] = true, -- Ambrose Boltspark
+ [34701] = true, -- Colosos
+ [34705] = true, -- Marshal Jacob Alerius
+ [34657] = true, -- Jaelyne Evensong
+ [34703] = true, -- Lana Stouthammer
+
+ -- Neutral
+ [34928] = true, -- Argent Confessor Paletress
+ [35119] = true, -- Eadric the Pure
+ [35451] = true, -- The Black Knight
+
+ -------------------------------------------------------------------------------
+ -- Trial of the Crusader
+ -------------------------------------------------------------------------------
+ [34796] = true, -- Gormok
+ [35144] = true, -- Acidmaw
+ [34799] = true, -- Dreadscale
+ [34797] = true, -- Icehowl
+
+ [34780] = true, -- Jaraxxus
+
+ [34461] = true, -- Tyrius Duskblade
+ [34460] = true, -- Kavina Grovesong
+ [34469] = true, -- Melador Valestrider
+ [34467] = true, -- Alyssia Moonstalker
+ [34468] = true, -- Noozle Whizzlestick
+ [34465] = true, -- Velanaa
+ [34471] = true, -- Baelnor Lightbearer
+ [34466] = true, -- Anthar Forgemender
+ [34473] = true, -- Brienna Nightfell
+ [34472] = true, -- Irieth Shadowstep
+ [34470] = true, -- Saamul
+ [34463] = true, -- Shaabad
+ [34474] = true, -- Serissa Grimdabbler
+ [34475] = true, -- Shocuul
+
+ [34458] = true, -- Gorgrim Shadowcleave
+ [34451] = true, -- Birana Stormhoof
+ [34459] = true, -- Erin Misthoof
+ [34448] = true, -- Ruj'kah
+ [34449] = true, -- Ginselle Blightslinger
+ [34445] = true, -- Liandra Suncaller
+ [34456] = true, -- Malithas Brightblade
+ [34447] = true, -- Caiphus the Stern
+ [34441] = true, -- Vivienne Blackwhisper
+ [34454] = true, -- Maz'dinah
+ [34444] = true, -- Thrakgar
+ [34455] = true, -- Broln Stouthorn
+ [34450] = true, -- Harkzog
+ [34453] = true, -- Narrhok Steelbreaker
+
+ [35610] = true, -- Cat
+ [35465] = true, -- Zhaagrym
+
+ [34497] = true, -- Fjola Lightbane
+ [34496] = true, -- Eydis Darkbane
+ [34564] = true, -- Anub'arak (Trial of the Crusader)
+
+ -------------------------------------------------------------------------------
+ -- Uldaman
+ -------------------------------------------------------------------------------
+ [7057] = true, -- Digmaster Shovelphlange
+ -- [2932] = true, -- Magregan Deepshadow (Outside the instance, not elite)
+ [6910] = true, -- Revelosh
+ [7228] = true, -- Ironaya
+ [7023] = true, -- Obsidian Sentinel
+ [7206] = true, -- Ancient Stone Keeper
+ [7291] = true, -- Galgann Firehammer
+ [4854] = true, -- Grimlok
+ [2748] = true, -- Archaedas
+ [6906] = true, -- Baelog
+
+ -------------------------------------------------------------------------------
+ -- Ulduar: The Antechamber of Ulduar
+ -------------------------------------------------------------------------------
+ [32867] = true, -- Steelbreaker
+ [32927] = true, -- Runemaster Molgeim
+ [32857] = true, -- Stormcaller Brundir
+ [32930] = true, -- Kologarn
+ [33515] = true, -- Auriaya
+ [34035] = true, -- Feral Defender
+ [32933] = true, -- Left Arm
+ [32934] = true, -- Right Arm
+ [33524] = true, -- Saronite Animus
+
+ -------------------------------------------------------------------------------
+ -- Ulduar: The Celestial Planetarium
+ -------------------------------------------------------------------------------
+ [32871] = true, -- Algalon the Observer
+
+ -------------------------------------------------------------------------------
+ -- Ulduar: The Descent into Madness
+ -------------------------------------------------------------------------------
+ [33271] = true, -- General Vezax
+ [33890] = true, -- Brain of Yogg-Saron
+ [33136] = true, -- Guardian of Yogg-Saron
+ [33288] = true, -- Yogg-Saron
+ [32915] = true, -- Elder Brightleaf
+ [32913] = true, -- Elder Ironbranch
+ [32914] = true, -- Elder Stonebark
+ [32882] = true, -- Jormungar Behemoth
+ [33432] = true, -- Leviathan Mk II
+ [34014] = true, -- Sanctum Sentry
+
+ -------------------------------------------------------------------------------
+ -- Ulduar: The Keepers of Ulduar
+ -------------------------------------------------------------------------------
+ [33350] = true, -- Mimiron
+ [32906] = true, -- Freya
+ [32865] = true, -- Thorim
+ [32845] = true, -- Hodir
+
+ -------------------------------------------------------------------------------
+ -- Ulduar: The Siege of Ulduar
+ -------------------------------------------------------------------------------
+ [33113] = true, -- Flame Leviathan
+ [33118] = true, -- Ignis the Furnace Master
+ [33186] = true, -- Razorscale
+ [33293] = true, -- XT-002 Deconstructor
+ [33670] = true, -- Aerial Command Unit
+ [33329] = true, -- Heart of the Deconstructor
+ [33651] = true, -- VX-001
+
+ -------------------------------------------------------------------------------
+ -- Un'Goro Crater
+ -------------------------------------------------------------------------------
+ [14461] = true, -- Baron Charr
+
+ -------------------------------------------------------------------------------
+ -- Upper Blackrock Spire
+ -------------------------------------------------------------------------------
+ [76413] = true, -- Orebender Gor'ashan
+ [76021] = true, -- Kyrak
+ [79912] = true, -- Commander Tharbek
+ [76585] = true, -- Ragewing the Untamed
+ [77120] = true, -- Warlord Zaela
+
+ -------------------------------------------------------------------------------
+ -- Utgarde Keep: Main Bosses
+ -------------------------------------------------------------------------------
+ [23953] = true, -- Prince Keleseth (Utgarde Keep)
+ [27390] = true, -- Skarvald the Constructor
+ [24200] = true, -- Skarvald the Constructor
+ [23954] = true, -- Ingvar the Plunderer
+ [23980] = true, -- Ingvar the Plunderer
+
+ -------------------------------------------------------------------------------
+ -- Utgarde Keep: Secondary Bosses
+ -------------------------------------------------------------------------------
+ [27389] = true, -- Dalronn the Controller
+ [24201] = true, -- Dalronn the Controller
+
+ -------------------------------------------------------------------------------
+ -- Utgarde Pinnacle
+ -------------------------------------------------------------------------------
+ [26668] = true, -- Svala Sorrowgrave
+ [26687] = true, -- Gortok Palehoof
+ [26693] = true, -- Skadi the Ruthless
+ [26861] = true, -- King Ymiron
+
+ -------------------------------------------------------------------------------
+ -- Wailing Caverns
+ -------------------------------------------------------------------------------
+ [5775] = true, -- Verdan the Everliving
+ [3670] = true, -- Lord Pythas
+ [3673] = true, -- Lord Serpentis
+ [3669] = true, -- Lord Cobrahn
+ [3654] = true, -- Mutanus the Devourer
+ [3674] = true, -- Skum
+ [3653] = true, -- Kresh
+ [3671] = true, -- Lady Anacondra
+ [5912] = true, -- Deviate Faerie Dragon
+ [3672] = true, -- Boahn, outside
+ [3655] = true, -- Mad Magglish, outside
+ [3652] = true, -- Trigore the Lasher, outside
+
+ -------------------------------------------------------------------------------
+ -- Vault of Archavon
+ -------------------------------------------------------------------------------
+ [31125] = true, -- Archavon the Stone Watcher
+ [33993] = true, -- Emalon the Storm Watcher
+ [35013] = true, -- Koralon the Flamewatcher
+ [38433] = true, -- Toravon the Ice Watcher
+
+ -------------------------------------------------------------------------------
+ -- Well of Eternity 4.3
+ -------------------------------------------------------------------------------
+
+ [55085] = true, -- Peroth'arn
+ [54853] = true, -- Queen Azshara
+ [54969] = true, -- Mannoroth
+ [55419] = true, -- Varo'then
+
+ -------------------------------------------------------------------------------
+ -- Winterspring
+ -------------------------------------------------------------------------------
+ [14457] = true, -- Princess Tempestria
+
+ -------------------------------------------------------------------------------
+ -- World Dragons
+ -------------------------------------------------------------------------------
+ [14889] = true, -- Emeriss
+ [14888] = true, -- Lethon
+ [14890] = true, -- Taerar
+ [14887] = true, -- Ysondre
+
+ -------------------------------------------------------------------------------
+ -- Zul'Aman
+ -------------------------------------------------------------------------------
+ -- Animal Bosses
+ [29024] = true, -- Nalorakk
+ [28514] = true, -- Nalorakk
+ [23576] = true, -- Nalorakk
+ [23574] = true, -- Akil'zon
+ [23578] = true, -- Jan'alai
+ [28515] = true, -- Jan'alai
+ [29023] = true, -- Jan'alai
+ [23577] = true, -- Halazzi
+ [28517] = true, -- Halazzi
+ [29022] = true, -- Halazzi
+ [24239] = true, -- Malacrass
+
+ -- Final Bosses
+ [23863] = true, -- Zul'jin
+
+ -------------------------------------------------------------------------------
+ -- Zul'Farrak
+ -------------------------------------------------------------------------------
+ [7267] = true, -- Chief Ukorz Sandscalp
+ [7271] = true, -- Witch Doctor Zum'rah
+ [7272] = true, -- Theka the Martyr
+ [7273] = true, -- Gahz'rilla
+ [7274] = true, -- Sandfury Executioner
+ [7275] = true, -- Shadowpriest Sezz'ziz
+ [7604] = true, -- Sergeant Bly
+ [7605] = true, -- Raven
+ [7606] = true, -- Oro Eyegouge
+ [7608] = true, -- Murta Grimgut
+ [7795] = true, -- Hydromancer Velratha
+ [7796] = true, -- Nekrum Gutchewer
+ [7797] = true, -- Ruuzlu
+ [8127] = true, -- Antu'sul
+ [10080] = true, -- Sandarr Dunereaver
+ [10082] = true, -- Zerillis
+ [10081] = true, -- Dustwraith
+
+ -------------------------------------------------------------------------------
+ -- Zul'Gurub - 4.1
+ -------------------------------------------------------------------------------
+ [52053] = true, -- Zanzil
+ [52059] = true, -- High Priestess Kilnara
+ [52148] = true, -- Jin'do the Godbreaker
+ [52151] = true, -- Bloodlord Mandokir
+ [52155] = true, -- High Priest Venoxis
+ --[52157] = true, -- Ohgan (Mandokirs Raptor) disabled by Mikk- this is an add, not a boss.
+ [52269] = true, -- Renataki
+ [52258] = true, -- Gri'lek
+ [52271] = true, -- Hazza'rah
+ [52286] = true, -- Wushoolay
+}
\ No newline at end of file
diff --git a/System/Lists/BurnUnits.lua b/System/Lists/BurnUnits.lua
index 06a1d15f74..11698f087f 100644
--- a/System/Lists/BurnUnits.lua
+++ b/System/Lists/BurnUnits.lua
@@ -4,6 +4,404 @@ if br.lists == nil then
end
-- burnUnits = List of UnitID/Names we should have highest prio on.
br.lists.burnUnits = {
+ --[[===========================]]
+ --[[ CLASSIC / VANILLA (60) ]]
+ --[[===========================]]
+
+ -- Stratholme
+ [10558] = { coef = 150, name = "Hearthsinger Forresten", id = 10558 }, -- Fears party
+ [10516] = { coef = 100, name = "The Unforgiven", id = 10516 }, -- High damage
+
+ -- Scholomance (Vanilla)
+ [10433] = { coef = 150, name = "Marduk Blackpool", id = 10433 }, -- Heals other mobs
+ [10498] = { coef = 100, name = "Spectral Tutor", id = 10498 }, -- AoE damage
+
+ -- Blackrock Depths
+ [9041] = { coef = 100, name = "Warder Stilgiss", id = 9041 }, -- Summons adds
+ [9018] = { coef = 150, name = "High Interrogator Gerstahn", id = 9018 }, -- Mind control
+
+ -- Lower Blackrock Spire
+ [9268] = { coef = 100, name = "Smolderweb Hatchling", id = 9268 }, -- Swarms
+ [10316] = { coef = 150, name = "Blackhand Incarcerator", id = 10316 }, -- High damage
+
+ -- Upper Blackrock Spire
+ [10742] = { coef = 150, name = "Blackhand Elite", id = 10742 }, -- Mortal Strike
+ [10814] = { coef = 100, name = "Chromatic Elite Guard", id = 10814 }, -- Shield other mobs
+
+ -- Dire Maul
+ [11480] = { coef = 100, name = "Arcane Aberration", id = 11480 }, -- Mana burn
+ [11483] = { coef = 150, name = "Mana Remnant", id = 11483 }, -- Explodes
+
+ -- Sunken Temple (Temple of Atal'Hakkar)
+ [5710] = { coef = 150, name = "Jammal'an the Prophet", id = 5710 }, -- Heals boss
+ [5717] = { coef = 100, name = "Mijan", id = 5717 }, -- Troll mini-boss
+
+ -- Maraudon
+ [13738] = { coef = 150, name = "Cavindra", id = 13738 }, -- High damage caster
+ [13741] = { coef = 100, name = "Gelk", id = 13741 }, -- AoE fear
+
+ -- Zul'Farrak
+ [7797] = { coef = 150, name = "Ruuzlu", id = 7797 }, -- Healer troll
+ [7795] = { coef = 100, name = "Hydromancer Velratha", id = 7795 }, -- Frost nova spam
+
+ -- Uldaman
+ [4854] = { coef = 150, name = "Grimlok", id = 4854 }, -- High damage
+ [6906] = { coef = 100, name = "Baelog", id = 6906 }, -- Dwarf trio
+
+ -- Molten Core
+ [11662] = { coef = 100, name = "Flamewaker Priest", id = 11662 }, -- Heals other adds
+ [11663] = { coef = 150, name = "Flamewaker Healer", id = 11663 }, -- Heals Lucifron/Gehennas
+
+ -- Blackwing Lair
+ [12053] = { coef = 200, name = "Blackwing Spellbinder", id = 12053 }, -- Greater Polymorph on tank
+ [12468] = { coef = 100, name = "Death Talon Hatcher", id = 12468 }, -- Spawns whelps
+
+ -- Ahn'Qiraj 40
+ [15316] = { coef = 150, name = "Obsidian Nullifier", id = 15316 }, -- Mana burn on healers
+ [15338] = { coef = 200, name = "Anubisath Sentinel", id = 15338 }, -- Transfers buffs to nearby allies
+ [15334] = { coef = 100, name = "Vekniss Hatchling", id = 15334 }, -- Swarms tank
+
+ -- Naxxramas (Classic)
+ [16803] = { coef = 100, name = "Deathknight Understudy", id = 16803 }, -- Heals Instructor Razuvious
+ [16146] = { coef = 150, name = "Death Knight", id = 16146 }, -- High damage
+ [16244] = { coef = 200, name = "Infectious Ghoul", id = 16244 }, -- Plague zone, spreads disease
+
+ --[[===========================]]
+ --[[ BURNING CRUSADE (70) ]]
+ --[[===========================]]
+
+ -- Shadow Labyrinth
+ [18642] = { coef = 150, name = "Fel Guardhound", id = 18642 }, -- Charge stun
+ [18663] = { coef = 100, name = "Maiden of Discipline", id = 18663 }, -- Whirlwind
+
+ -- Shattered Halls
+ [20923] = { coef = 150, name = "Blood Guard Porung", id = 20923 }, -- Shield Wall
+ [17670] = { coef = 100, name = "Shattered Hand Warhound", id = 17670 }, -- Debuff bleed
+
+ -- Steamvault
+ [17954] = { coef = 100, name = "Coilfang Soothsayer", id = 17954 }, -- Heals
+ [17721] = { coef = 150, name = "Murloc Lurker", id = 17721 }, -- Swarm
+
+ -- Botanica
+ [19486] = { coef = 150, name = "Sunseeker Chemist", id = 19486 }, -- Unstable Chemicals explosion
+ [21579] = { coef = 100, name = "Summoned Bloodwarder Mender", id = 21579 }, -- Heals
+
+ -- Arcatraz
+ [20869] = { coef = 150, name = "Arcatraz Sentinel", id = 20869 }, -- High armor
+ [20886] = { coef = 100, name = "Wrath-Scryer Soccothrates", id = 20886 }, -- Fear
+
+ -- Magisters' Terrace
+ [24683] = { coef = 200, name = "Sunblade Mage Guard", id = 24683 }, -- Glyph of Power buff
+ [24685] = { coef = 150, name = "Sunblade Magister", id = 24685 }, -- Frostbolt spam
+
+ -- Slave Pens
+ [17816] = { coef = 150, name = "Bogstrok", id = 17816 }, -- High damage
+ [21338] = { coef = 100, name = "Coilfang Leper", id = 21338 }, -- Disease spreader
+
+ -- Underbog
+ [17827] = { coef = 150, name = "Claw", id = 17827 }, -- Bog Lord pet, high damage
+ [17826] = { coef = 100, name = "Swamplord Musel'ek", id = 17826 }, -- Hunter boss
+
+ -- Mana-Tombs
+ [18493] = { coef = 200, name = "Auchenai Soulpriest", id = 18493 }, -- Turns heals into damage
+ [18371] = { coef = 150, name = "Shirrak the Dead Watcher", id = 18371 }, -- Inhibit Magic
+
+ -- Auchenai Crypts
+ [18524] = { coef = 150, name = "Angered Skeleton", id = 18524 }, -- Enrage
+ [18472] = { coef = 100, name = "Auchenai Monk", id = 18472 }, -- Shadow Mend
+
+ -- Sethekk Halls
+ [18322] = { coef = 200, name = "Sethekk Ravenguard", id = 18322 }, -- Bloodthirst
+ [18473] = { coef = 150, name = "Sethekk Oracle", id = 18473 }, -- Arcane Lightning
+
+ -- Old Hillsbrad Foothills
+ [17848] = { coef = 150, name = "Lieutenant Drake", id = 17848 }, -- Event mob
+ [18092] = { coef = 100, name = "Tarren Mill Guardsman", id = 18092 }, -- Swarm adds
+
+ -- Black Morass
+ [17880] = { coef = 200, name = "Infinite Assassin", id = 17880 }, -- Must kill before portal closes
+ [17835] = { coef = 150, name = "Infinite Chronomancer", id = 17835 }, -- Heals other adds
+
+ -- Mechanar
+ [19710] = { coef = 150, name = "Bloodwarder Physician", id = 19710 }, -- Healer
+ [20059] = { coef = 100, name = "Sunseeker Astromage", id = 20059 }, -- Fireball
+
+ -- Blood Furnace
+ [17491] = { coef = 150, name = "Laughing Skull Rogue", id = 17491 }, -- Hemorrhage bleed
+ [17653] = { coef = 100, name = "Shadowmoon Channeler", id = 17653 }, -- Shadow Bolt Volley
+
+ -- Hellfire Ramparts
+ [17264] = { coef = 150, name = "Bonechewer Ravener", id = 17264 }, -- Enrage
+ [17270] = { coef = 100, name = "Bleeding Hollow Darkcaster", id = 17270 }, -- Shadow Bolt
+
+ -- Karazhan
+ [17007] = { coef = 150, name = "Concubine", id = 17007 }, -- Prince Malchezaar adds
+ [16809] = { coef = 100, name = "Wanton Hostess", id = 16809 }, -- Moroes adds
+
+ -- Black Temple
+ [22964] = { coef = 200, name = "Sister of Pleasure", id = 22964 }, -- Mother Shahraz adds, heal
+ [23047] = { coef = 150, name = "Parasitic Shadowfiend", id = 23047 }, -- Teron Gorefiend constructs
+
+ -- Sunwell Plateau
+ [25367] = { coef = 200, name = "Enslaved Servant", id = 25367 }, -- Kalecgos demon phase add
+ [25840] = { coef = 150, name = "Entropius", id = 25840 }, -- M'uru dark phase
+
+ --[[===========================]]
+ --[[ WRATH OF THE LICH KING (80) ]]
+ --[[===========================]]
+
+ -- Utgarde Keep
+ [23970] = { coef = 100, name = "Vrykul Skeleton", id = 23970 }, -- High damage
+ [24201] = { coef = 150, name = "Proto-Drake Handler", id = 24201 }, -- Summons drakes
+
+ -- Nexus
+ [26763] = { coef = 150, name = "Anomalus Chaotic Rift", id = 26763 }, -- Rift spawner
+ [26716] = { coef = 100, name = "Crazed Mana-Wraith", id = 26716 }, -- Frenzy
+
+ -- Azjol-Nerub
+ [28995] = { coef = 100, name = "Anub'ar Champion", id = 28995 }, -- Pound
+ [29217] = { coef = 150, name = "Watcher Gashra", id = 29217 }, -- Curse
+
+ -- Ahn'kahet: The Old Kingdom
+ [30285] = { coef = 150, name = "Forgotten One", id = 30285 }, -- High damage
+ [29310] = { coef = 100, name = "Ahn'kahar Spell Flinger", id = 29310 }, -- Shadow Bolt Volley
+
+ -- Drak'Tharon Keep
+ [26627] = { coef = 150, name = "Crystal Handler", id = 26627 }, -- Shatters to spawn adds
+ [27598] = { coef = 100, name = "Fetid Troll Corpse", id = 27598 }, -- Explodes
+
+ -- Violet Hold
+ [29315] = { coef = 200, name = "Portal Guardian", id = 29315 }, -- Must kill to close portal
+ [30695] = { coef = 150, name = "Azure Invader", id = 30695 }, -- Portal adds
+
+ -- Gundrak
+ [29573] = { coef = 100, name = "Drakkari Bat", id = 29573 }, -- Swarm
+ [29920] = { coef = 150, name = "Drakkari Colossus", id = 29920 }, -- High armor
+
+ -- Halls of Stone
+ [27977] = { coef = 200, name = "Tribunal of Ages", id = 27977 }, -- Event waves
+ [28070] = { coef = 100, name = "Dark Rune Protector", id = 28070 }, -- Cleave
+
+ -- Halls of Lightning
+ [28579] = { coef = 150, name = "Dark Rune Champion", id = 28579 }, -- Whirlwind
+ [28965] = { coef = 100, name = "Volkhan Golem", id = 28965 }, -- Shatter explosion
+
+ -- Oculus
+ [27755] = { coef = 150, name = "Centrifuge Construct", id = 27755 }, -- Empowering Blows
+ [27447] = { coef = 100, name = "Mage-Lord Urom", id = 27447 }, -- Time Bomb
+
+ -- Culling of Stratholme
+ [28199] = { coef = 150, name = "Chrono-Lord Epoch", id = 28199 }, -- Time Warp
+ [28167] = { coef = 100, name = "Infinite Adversary", id = 28167 }, -- Banish healer
+
+ -- Utgarde Pinnacle
+ [26687] = { coef = 150, name = "Gortok Palehoof", id = 26687 }, -- High damage
+ [27281] = { coef = 100, name = "Scourge Hulk", id = 27281 }, -- Volatile Infection
+
+ -- Trial of the Champion
+ [35314] = { coef = 200, name = "Eressea Dawnsinger", id = 35314 }, -- Heals
+ [35328] = { coef = 150, name = "Argent Lightwielder", id = 35328 }, -- Divine Storm
+
+ -- Pit of Saron
+ [36494] = { coef = 150, name = "Wrathbone Laborer", id = 36494 }, -- Puncture Wound bleed
+ [36661] = { coef = 100, name = "Ymirjar Deathbringer", id = 36661 }, -- Banish
+
+ -- Forge of Souls
+ [36516] = { coef = 150, name = "Soul Horror", id = 36516 }, -- Soul Strike
+ [36522] = { coef = 100, name = "Soulguard Animator", id = 36522 }, -- Summons adds
+
+ -- Halls of Reflection
+ [38177] = { coef = 200, name = "Shadowy Mercenary", id = 38177 }, -- High damage, event waves
+ [38176] = { coef = 150, name = "Tortured Rifleman", id = 38176 }, -- Shoot spam
+
+ -- Naxxramas (Wrath) / Ahn'kahet
+ [30176] = { coef = 100, name = "Ahn'kahar Guardian", id = 30176 }, -- Gives immunity buff to boss (Ahn'kahet)
+ [29701] = { coef = 150, name = "Crypt Guard", id = 29701 }, -- High armor, priority
+
+ -- Ulduar
+ [33524] = { coef = 200, name = "Dark Rune Guardian", id = 33524 }, -- Runic Barrier buff
+ [33722] = { coef = 150, name = "Rubble", id = 33722 }, -- Kologarn adds
+ [33526] = { coef = 100, name = "Dark Rune Sentinel", id = 33526 }, -- Whirlwind
+
+ -- Trial of the Crusader
+ [34496] = { coef = 200, name = "Mistress of Pain", id = 34496 }, -- Jaraxxus add
+ [34451] = { coef = 150, name = "Burning Treant", id = 34451 }, -- Freya's Gift add
+
+ -- Icecrown Citadel
+ [37890] = { coef = 200, name = "Cult Adherent", id = 37890 }, -- Lady Deathwhisper, shields boss
+ [37934] = { coef = 150, name = "Cult Fanatic", id = 37934 }, -- Lady Deathwhisper
+ [36672] = { coef = 200, name = "Kor'kron Axethrower", id = 36672 }, -- Gunship, sabotage
+ [37098] = { coef = 150, name = "Val'kyr Shadowguard", id = 37098 }, -- Lich King phase transition
+ [36701] = { coef = 100, name = "Raging Spirit", id = 36701 }, -- Deathbringer Saurfang
+
+ -- Ruby Sanctum
+ [39751] = { coef = 150, name = "Baltharus the Warborn Clone", id = 39751 }, -- Split mechanic
+
+ --[[===========================]]
+ --[[ CATACLYSM (85) ]]
+ --[[===========================]]
+
+ -- Throne of the Tides
+ [40788] = { coef = 150, name = "Gilgoblin Hunter", id = 40788 }, -- Poisoned Spear
+ [40825] = { coef = 100, name = "Unstable Corruption", id = 40825 }, -- Explodes
+
+ -- Blackrock Caverns
+ [39392] = { coef = 150, name = "Defiled Earth Rager", id = 39392 }, -- Chains of Woe
+ [39294] = { coef = 100, name = "Twilight Flame Caller", id = 39294 }, -- Blast Wave
+
+ -- Vortex Pinnacle
+ [45912] = { coef = 200, name = "Wild Vortex", id = 45912 }, -- Cyclone spawn
+ [45477] = { coef = 100, name = "Gust Soldier", id = 45477 }, -- Wind Blast
+
+ -- Stonecore
+ [43438] = { coef = 150, name = "Crystalspawn Giant", id = 43438 }, -- Quake stomp
+ [42691] = { coef = 100, name = "Stonecore Berserker", id = 42691 }, -- Spinning Slash
+
+ -- Lost City of the Tol'vir
+ [44926] = { coef = 150, name = "Frenzied Crocolisk", id = 44926 }, -- Vicious Bite
+ [45124] = { coef = 100, name = "Oathsworn Tamer", id = 45124 }, -- Summons adds
+
+ -- Halls of Origination
+ [39378] = { coef = 200, name = "Sun-Touched Servant", id = 39378 }, -- Blessing of the Sun immunity
+ [39366] = { coef = 100, name = "Spatial Anomaly", id = 39366 }, -- Wormhole spawn
+
+ -- Grim Batol
+ [39625] = { coef = 150, name = "Twilight Beguiler", id = 39625 }, -- Deceitful Blast
+ [39956] = { coef = 100, name = "Azureborne Seer", id = 39956 }, -- AoE heal
+
+ -- Deadmines (Heroic)
+ [48231] = { coef = 150, name = "Defias Enforcer", id = 48231 }, -- Bloodbath
+ [48417] = { coef = 100, name = "Defias Blood Wizard", id = 48417 }, -- Ragezone
+
+ -- Shadowfang Keep (Heroic)
+ [3865] = { coef = 150, name = "Shadow Charger", id = 3865 }, -- High mobility
+ [3872] = { coef = 100, name = "Deathsworn Captain", id = 3872 }, -- Raise Dead
+
+ -- Zul'Aman (Heroic)
+ [23596] = { coef = 200, name = "Amani'shi Flame Caster", id = 23596 }, -- Fire damage
+ [24549] = { coef = 150, name = "Amani'shi Medicine Man", id = 24549 }, -- Heals
+
+ -- Zul'Gurub (Heroic)
+ [52053] = { coef = 200, name = "Tiki Lord Mu'Loa", id = 52053 }, -- Voodoo Bolt
+ [52157] = { coef = 150, name = "Gurubashi Shadow Hunter", id = 52157 }, -- Shadowy Protector
+
+ -- End Time
+ [54431] = { coef = 150, name = "Echo of Baine", id = 54431 }, -- Pulverize
+ [54442] = { coef = 100, name = "Echo of Tyrande", id = 54442 }, -- Stardust spawn
+
+ -- Well of Eternity
+ [55085] = { coef = 150, name = "Dreadlord Defender", id = 55085 }, -- Carrion Swarm
+ [55503] = { coef = 100, name = "Enchanted Highborne", id = 55503 }, -- Frost Bolt Volley
+
+ -- Hour of Twilight
+ [54590] = { coef = 200, name = "Twilight Assassin", id = 54590 }, -- Backstab spam
+ [54634] = { coef = 150, name = "Twilight Shadow Walker", id = 54634 }, -- Shadow Bolt
+
+ -- Blackwing Descent
+ [41440] = { coef = 200, name = "Aberration", id = 41440 }, -- Maloriak adds, must burn
+ [41545] = { coef = 150, name = "Golem Sentry", id = 41545 }, -- Magmaw spike adds
+
+ -- Bastion of Twilight
+ [46147] = { coef = 200, name = "Twilight Sentry", id = 46147 }, -- Portals phase
+ [44653] = { coef = 150, name = "Chogall Adherent", id = 44653 }, -- Heal/damage adds
+
+ -- Throne of the Four Winds
+ [46753] = { coef = 200, name = "Al'Akir Stormling", id = 46753 }, -- Phase 1 adds
+
+ -- Firelands
+ [53695] = { coef = 200, name = "Molten Flare", id = 53695 }, -- Alysrazor air phase
+ [53216] = { coef = 150, name = "Blazing Talon Initiate", id = 53216 }, -- Alysrazor ground adds
+ [53494] = { coef = 100, name = "Harbinger of Flame", id = 53494 }, -- Rhyolith adds
+
+ -- Dragon Soul
+ [55265] = { coef = 200, name = "Unstable Corruption", id = 55265 }, -- Spine of Deathwing
+ [55266] = { coef = 150, name = "Corrupted Blood", id = 55266 }, -- Spine of Deathwing
+ [56471] = { coef = 200, name = "Blistering Tentacle", id = 56471 }, -- Madness of Deathwing
+ [56846] = { coef = 150, name = "Elementium Bolt", id = 56846 }, -- Spine mechanics
+
+ --[[===========================]]
+ --[[ MISTS OF PANDARIA CLASSIC ]]
+ --[[===========================]]
+
+ -- Stormstout Brewery
+ [59545] = { coef = 100, name = "Hozen Bouncer", id = 59545 },
+ [56637] = { coef = 150, name = "Ook-Ook", buff = 106807 }, -- When Going Bananas cast
+ [59579] = { coef = 100, name = "Sodden Hozen Brawler", id = 59579 },
+
+ -- Mogu'shan Palace
+ [61340] = { coef = 100, name = "Undying Flame", id = 61340 }, -- Xin the Weaponmaster adds
+ [61339] = { coef = 100, name = "Animated Staff", id = 61339 },
+
+ -- Shado-Pan Monastery
+ [56747] = { coef = 150, name = "Gu Cloudstrike", buff = 110945 }, -- While Charging Soul
+ [56884] = { coef = 100, name = "Consuming Sha", id = 56884 },
+ [59051] = { coef = 100, name = "Corrupt Living Water", id = 59051 },
+ [59764] = { coef = 150, name = "Sha Manifestation", id = 59764 },
+
+ -- Gate of the Setting Sun
+ [56906] = { coef = 100, name = "Volatile Munitions", id = 56906 }, -- Bombs
+ [56877] = { coef = 150, name = "Saboteur Kip'tilak", cast = 107268 }, -- Interrupt or burn
+
+ -- Siege of Niuzao Temple
+ [61699] = { coef = 100, name = "Sap Puddle", id = 61699 },
+ [61634] = { coef = 100, name = "Amber-Trapped Kunchong", id = 61634 },
+ [61398] = { coef = 150, name = "Sik'thik Amber-Sapper", id = 61398 },
+
+ -- Scarlet Halls
+ [59191] = { coef = 100, name = "Reinforced Archery Target", id = 59191 },
+ [59190] = { coef = 100, name = "Starving Hound", buff = 111853 }, -- While Obedience buff active
+
+ -- Scarlet Monastery
+ [59223] = { coef = 150, name = "Empowering Spirit", id = 59223 }, -- Heals boss
+ [59150] = { coef = 100, name = "Scarlet Judicator", cast = 113436 }, -- Burn or interrupt
+
+ -- Scholomance
+ [58823] = { coef = 150, name = "Bored Student", buff = 114062 }, -- If Rising Anger is up
+ [59184] = { coef = 100, name = "Boneguard Lieutenant", id = 59184 },
+ [59100] = { coef = 200, name = "Expired Test Subject", id = 59100 }, -- Explodes on death
+
+ -- Mogu'shan Vaults (Raid)
+ [60447] = { coef = 100, name = "Petrifying Stone Guard", id = 60447 },
+ [60043] = { coef = 100, name = "Sorcerer-King Mogu", id = 60043 },
+ [60399] = { coef = 150, name = "Ancient Mogu Machine", id = 60399 },
+
+ -- Heart of Fear (Raid)
+ [62511] = { coef = 200, name = "Overwhelming Assault", id = 62511 }, -- Blade Lord Ta'yak
+ [63591] = { coef = 150, name = "Corrupted Quickening", id = 63591 },
+ [62968] = { coef = 100, name = "Wind Lord Mel'jarak Adds", id = 62968 },
+
+ -- Terrace of Endless Spring (Raid)
+ [62358] = { coef = 100, name = "Haunting Sha", id = 62358 },
+ [62919] = { coef = 150, name = "Reflection", id = 62919 }, -- Lei Shi encounter
+ [60999] = { coef = 100, name = "Sha of Fear Adds", id = 60999 },
+
+ -- Throne of Thunder (Raid)
+ [69427] = { coef = 100, name = "Waterspout", id = 69427 }, -- Council of Elders
+ [69649] = { coef = 150, name = "Living Sand", id = 69649 }, -- Horridon adds
+ [69134] = { coef = 200, name = "Dire Troll", id = 69134 }, -- Council of Elders
+ [69633] = { coef = 100, name = "Sul Lithuz Stonegazer", id = 69633 },
+ [68220] = { coef = 100, name = "Spirit Flayer", id = 68220 }, -- Primordius adds
+ [70249] = { coef = 200, name = "Crimsonscale Firestorm", id = 70249 }, -- Ji-Kun fight
+ [70216] = { coef = 100, name = "Nest Guardian", id = 70216 },
+ [69680] = { coef = 150, name = "Zandalari Dinomancer", id = 69680 }, -- Horridon
+ [69692] = { coef = 100, name = "Zandalari Warlord", id = 69692 },
+
+ -- Siege of Orgrimmar (Raid)
+ [71984] = { coef = 200, name = "Minion of Y'Shaarj", id = 71984 }, -- Sha of Pride adds
+ [71152] = { coef = 150, name = "Embodied Gloom", id = 71152 },
+ [71159] = { coef = 100, name = "Embodied Anguish", id = 71159 },
+ [71068] = { coef = 100, name = "Embodied Doubt", id = 71068 },
+ [71858] = { coef = 200, name = "Sniper", id = 71858 }, -- Malkorok encounter
+ [71995] = { coef = 150, name = "Kor'kron Demolisher", id = 71995 },
+ [72272] = { coef = 100, name = "Dread Spawn", id = 72272 }, -- Dark Shaman
+ [73223] = { coef = 200, name = "Amber Parasite", id = 73223 }, -- Paragons encounter
+ [71934] = { coef = 100, name = "Kor'kron Assassin", id = 71934 },
+ [71953] = { coef = 100, name = "Korkron Reaper", id = 71953 },
+
+ --[[===========================]]
+ --[[ LATER EXPANSIONS (KEEP) ]]
+ --[[===========================]]
+
--Vault of the Incarnates
[199353] = { coef = 100, name = "Batak", id = 199353 },
[192934] = { coef = 100, name = "Volatile Infuser", id = 192934 },
@@ -76,8 +474,7 @@ br.lists.burnUnits = {
-- Sanguine Depth
[168882] = { coef = 100, name = "Fleeting Manifestation", id = 168882 }, -- 168882/fleeting-manifestation - must kill fast
- -- old content stuff
- [30176] = { coef = 100, name = "Ahn'kahar Guardian", id = 30176 }, -- Gives boss and other add immune buff while alive.
+ -- WoD Dungeons
[71603] = { coef = 100, name = "Immerseus Oozes", id = 71603 }, -- kill on sight
-- Shadowmoon Burial Grounds
[75966] = { coef = 100, name = "Defiled Spirit", id = 75966 }, -- need to be cc and snared and is not allowed to reach boss.
diff --git a/System/Lists/CCUnits.lua b/System/Lists/CCUnits.lua
index dcfcb9cf87..a78faf0628 100644
--- a/System/Lists/CCUnits.lua
+++ b/System/Lists/CCUnits.lua
@@ -5,6 +5,244 @@ end
-- Crowd Control Units = list of units to stun, either always or under certain condition such as having a buff or whirlwind etc
-- example
br.lists.ccUnits = {
+ --[[===========================]]
+ --[[ CLASSIC / VANILLA (60) ]]
+ --[[===========================]]
+
+ -- Stratholme
+ [10558] = {{ name = "Hearthsinger Forresten", spell = 16798 }}, -- Fear
+ [10813] = {{ name = "Balnazzar", spell = 17405 }}, -- Domination mind control
+
+ -- Scholomance (Vanilla)
+ [10433] = {{ name = "Marduk Blackpool", spell = 16097 }}, -- Hex
+ [10499] = {{ name = "Spectral Researcher", spell = 16799 }}, -- Mind Flay
+
+ -- Blackrock Depths
+ [9018] = {{ name = "High Interrogator Gerstahn", spell = 15063 }}, -- Mind Control
+ [9024] = {{ name = "Pyromancer Loregrain", spell = 15744 }}, -- Blast Wave
+
+ -- Upper Blackrock Spire
+ [10742] = {{ name = "Blackhand Elite", spell = 15284 }}, -- Mortal Strike (high damage)
+ [9693] = {{ name = "Burning Felguard", spell = 15532 }}, -- Frost Nova
+
+ -- Sunken Temple
+ [5710] = {{ name = "Jammal'an the Prophet", spell = 12480 }}, -- Heal
+ [5717] = {{ name = "Mijan", spell = 11428 }}, -- Knockdown
+
+ -- Maraudon
+ [13738] = {{ name = "Cavindra", spell = 21869 }}, -- Flame Shock
+ [13741] = {{ name = "Gelk", spell = 21869 }}, -- Fear
+
+ -- Zul'Farrak
+ [7797] = {{ name = "Ruuzlu", spell = 11642 }}, -- Heal
+ [7796] = {{ name = "Nekrum Gutchewer", spell = 8600 }}, -- Fevered Plague
+
+ -- Uldaman
+ [6906] = {{ name = "Baelog", spell = 6524 }}, -- Ground Tremor
+ [4854] = {{ name = "Grimlok", spell = 8148 }}, -- Demoralizing Shout
+
+ -- Molten Core
+ [11662] = {{ name = "Flamewaker Priest", spell = 19776 }}, -- Shadow Word: Pain spam
+
+ -- Blackwing Lair
+ [12053] = {{ name = "Blackwing Spellbinder", spell = 16793 }}, -- Greater Polymorph on tanks
+
+ -- Naxxramas
+ [16244] = {{ name = "Infectious Ghoul" }}, -- Plague spreader, CC priority
+ [16146] = {{ name = "Deathknight", spell = 19643 }}, -- High damage Mortal Strike
+
+ --[[===========================]]
+ --[[ BURNING CRUSADE (70) ]]
+ --[[===========================]]
+
+ -- Shadow Labyrinth
+ [18667] = {{ name = "Blackheart the Inciter", spell = 33676 }}, -- Incite Chaos mind control
+ [18663] = {{ name = "Maiden of Discipline", spell = 33787 }}, -- Whirlwind
+
+ -- Shattered Halls
+ [17670] = {{ name = "Shattered Hand Warhound", spell = 30636 }}, -- Rend (bleed)
+ [20923] = {{ name = "Blood Guard Porung", spell = 31901 }}, -- Shield Wall
+
+ -- Botanica
+ [19486] = {{ name = "Sunseeker Chemist", spell = 34639 }}, -- Unstable Chemicals
+ [21579] = {{ name = "Bloodwarder Mender", spell = 34649 }}, -- Heal
+
+ -- Magisters' Terrace
+ [24683] = {{ name = "Sunblade Mage Guard", buff = 46757 }}, -- Glyph of Power buff
+ [24685] = {{ name = "Sunblade Magister", spell = 46035 }}, -- Frostbolt
+
+ -- Mana-Tombs
+ [18493] = {{ name = "Auchenai Soulpriest", spell = 32860 }}, -- Turns heals into damage
+ [18371] = {{ name = "Shirrak the Dead Watcher", spell = 32361 }}, -- Inhibit Magic
+
+ -- Sethekk Halls
+ [18322] = {{ name = "Sethekk Ravenguard", spell = 32674 }}, -- Bloodthirst
+ [18473] = {{ name = "Sethekk Oracle", spell = 32129 }}, -- Arcane Lightning
+
+ -- Old Hillsbrad Foothills
+ [17848] = {{ name = "Lieutenant Drake", spell = 33834 }}, -- Exploding Shot
+
+ -- Black Morass
+ [17880] = {{ name = "Infinite Assassin", spell = 33865 }}, -- Backstab
+ [17835] = {{ name = "Infinite Chronomancer", spell = 33860 }}, -- Heal
+
+ -- Slave Pens / Underbog
+ [17827] = {{ name = "Claw", spell = 31429 }}, -- Claw Rage
+ [21337] = {{ name = "Coilfang Tempest", spell = 37159 }}, -- Lightning Bolt
+
+ -- Blood Furnace
+ [17491] = {{ name = "Laughing Skull Rogue", spell = 30992 }}, -- Hemorrhage
+
+ -- Hellfire Ramparts
+ [17264] = {{ name = "Bonechewer Ravener", spell = 8599 }}, -- Enrage
+
+ -- Karazhan
+ [16809] = {{ name = "Wanton Hostess", spell = 29321 }}, -- Fear on tank
+
+ -- Black Temple
+ [22964] = {{ name = "Sister of Pleasure", spell = 41338 }}, -- Heal
+
+ --[[===========================]]
+ --[[ WRATH OF THE LICH KING (80) ]]
+ --[[===========================]]
+
+ -- Violet Hold
+ [29315] = {{ name = "Portal Guardian" }}, -- Must CC/kill to close portals
+ [30695] = {{ name = "Azure Invader", spell = 58471 }}, -- Brutal Strike
+
+ -- Halls of Lightning
+ [28579] = {{ name = "Dark Rune Champion", spell = 52719 }}, -- Whirlwind
+ [28965] = {{ name = "Volkhan Golem", spell = 52237 }}, -- Shatter (explodes)
+
+ -- Culling of Stratholme
+ [28167] = {{ name = "Infinite Adversary", spell = 52854 }}, -- Banish
+ [28169] = {{ name = "Infinite Agent", spell = 52942 }}, -- Temporal Rift
+
+ -- Trial of the Champion
+ [35314] = {{ name = "Eressea Dawnsinger", spell = 66099 }}, -- Holy Smite spam
+ [35328] = {{ name = "Argent Lightwielder", spell = 66920 }}, -- Divine Storm
+
+ -- Pit of Saron
+ [36494] = {{ name = "Wrathbone Laborer", spell = 69579 }}, -- Puncture Wound
+ [36661] = {{ name = "Ymirjar Deathbringer", spell = 69603 }}, -- Banish
+
+ -- Halls of Reflection
+ [38177] = {{ name = "Shadowy Mercenary", spell = 72333 }}, -- Deadly Poison
+ [38176] = {{ name = "Tortured Rifleman", spell = 72340 }}, -- Shoot
+
+ -- Naxxramas
+ [16803] = {{ name = "Deathknight Understudy" }}, -- Always CC to prevent healing Razuvious
+
+ -- Ulduar
+ [33722] = {{ name = "Rubble", spell = 63849 }}, -- Stone Grip
+
+ -- Icecrown Citadel
+ [37890] = {{ name = "Cult Adherent", spell = 70842 }}, -- Dark Martyrdom (shields boss)
+ [37934] = {{ name = "Cult Fanatic", spell = 70645 }}, -- Dark Transformation
+
+ --[[===========================]]
+ --[[ CATACLYSM (85) ]]
+ --[[===========================]]
+
+ -- Throne of the Tides
+ [40788] = {{ name = "Gilgoblin Hunter", spell = 76622 }}, -- Poisoned Spear
+ [40825] = {{ name = "Unstable Corruption" }}, -- Must CC before it explodes
+
+ -- Vortex Pinnacle
+ [45912] = {{ name = "Wild Vortex" }}, -- Cyclone, must CC/kill
+ [45477] = {{ name = "Gust Soldier", spell = 88194 }}, -- Wind Blast
+
+ -- Stonecore
+ [43438] = {{ name = "Crystalspawn Giant", spell = 81008 }}, -- Quake stomp
+ [42691] = {{ name = "Stonecore Berserker", spell = 81637 }}, -- Spinning Slash
+
+ -- Grim Batol
+ [39625] = {{ name = "Twilight Beguiler", spell = 76711 }}, -- Deceitful Blast
+ [39854] = {{ name = "Azureborne Seer", spell = 76121 }}, -- Warped Twilight
+
+ -- Zul'Aman (Heroic)
+ [23596] = {{ name = "Amani'shi Flame Caster", spell = 43140 }}, -- Flame Breath
+ [24549] = {{ name = "Amani'shi Medicine Man", spell = 43432 }}, -- Heal
+
+ -- Zul'Gurub (Heroic)
+ [52053] = {{ name = "Tiki Lord Mu'Loa", spell = 96689 }}, -- Voodoo Bolt
+ [52157] = {{ name = "Gurubashi Shadow Hunter", spell = 96684 }}, -- Shadowy Protector
+
+ -- Hour of Twilight
+ [54590] = {{ name = "Twilight Assassin", spell = 103633 }}, -- Backstab
+ [54634] = {{ name = "Twilight Shadow Walker", spell = 103641 }}, -- Shadow Bolt
+
+ -- Blackwing Descent
+ [41440] = {{ name = "Aberration" }}, -- Must CC/burn, phase mechanic
+
+ -- Firelands
+ [53216] = {{ name = "Blazing Talon Initiate", spell = 99432 }}, -- Fieroblast
+
+ --[[===========================]]
+ --[[ MISTS OF PANDARIA CLASSIC ]]
+ --[[===========================]]
+
+ -- Temple of the Jade Serpent
+ [59546] = {{ name = "Figment of Doubt", spell = 106113 }}, -- When casting Doubt
+ [56762] = {{ name = "Corrupt Living Water", spell = 106797 }}, -- When casting Hydrolance
+
+ -- Stormstout Brewery
+ [59555] = {{ name = "Haunting Sha", spell = 114646 }}, -- When Haunting Gaze channels
+ [59797] = {{ name = "Yeasty Alemental", spell = 116027 }}, -- Brew Bolt
+
+ -- Mogu'shan Palace
+ [61340] = {{ name = "Undying Flame" }}, -- Always CC to prevent overwhelming adds
+ [61399] = {{ name = "Whirling Dervish", spell = 119981 }}, -- Mogu Whirlwind
+
+ -- Shado-Pan Monastery
+ [59547] = {{ name = "Shado-Pan Novice", buff = 115010 }}, -- When Charging Soul is active
+ [59544] = {{ name = "Fragment of Hatred", spell = 115002 }}, -- During Rising Hate cast
+ [59545] = {{ name = "Gripping Hatred", spell = 115002 }}, -- During Rising Hate cast
+ [56541] = {{ name = "Sha Manifestation", spell = 111585 }}, -- Consuming Bite
+
+ -- Gate of the Setting Sun
+ [56906] = {{ name = "Volatile Munitions" }}, -- Always CC or interrupt before explosion
+ [56589] = {{ name = "Sik'thik Guardian", spell = 107120 }}, -- When Impaling Strike
+
+ -- Siege of Niuzao Temple
+ [61449] = {{ name = "Sik'thik Soldier", spell = 121282 }}, -- When Devastating Arc channels
+ [62693] = {{ name = "Sik'thik Demolisher", spell = 119610 }}, -- Bombard cast
+
+ -- Scarlet Halls
+ [59150] = {{ name = "Scarlet Judicator", spell = 113436 }}, -- Heroic Defense cast (damage reduction)
+ [59240] = {{ name = "Scarlet Treasurer", spell = 113959 }}, -- When channeling heal
+
+ -- Scarlet Monastery
+ [59300] = {{ name = "Scarlet Myrmidon", spell = 115143 }}, -- Dragon Strike windup
+ [59746] = {{ name = "Fuel Barrel" }}, -- CC to prevent explosions
+
+ -- Scholomance
+ [58823] = {{ name = "Bored Student", buff = 114062 }}, -- When Rising Anger is up
+ [58757] = {{ name = "Failed Student", spell = 113134 }}, -- Explosive Pain cast
+ [58822] = {{ name = "Instructor Chillheart", spell = 113131 }}, -- Ice Wrath
+
+ -- Mogu'shan Vaults (Raid)
+ [60447] = {{ name = "Stone Guard", spell = 116529 }}, -- When overloading
+ [60397] = {{ name = "Quillen Guardian", spell = 117570 }}, -- Devastating Arc
+
+ -- Heart of Fear (Raid)
+ [62837] = {{ name = "Blade Lord Ta'yak", spell = 123474 }}, -- When casting Overwhelming Assault windup
+ [63591] = {{ name = "Manticore", spell = 123816 }}, -- Fear cast
+
+ -- Throne of Thunder (Raid)
+ [69633] = {{ name = "Sul Lithuz Stonegazer", spell = 137261 }}, -- Stone Gaze channel
+ [69680] = {{ name = "Zandalari Dinomancer", spell = 136954 }}, -- Dino Form buff (heal/damage increase)
+ [70216] = {{ name = "Nest Guardian", spell = 138923 }}, -- Talon Rake windup
+
+ -- Siege of Orgrimmar (Raid)
+ [71152] = {{ name = "Embodied Gloom", spell = 144579 }}, -- When channeling Black Cleave
+ [71995] = {{ name = "Kor'kron Demolisher", spell = 143639 }}, -- Explosive Tar cast
+ [72272] = {{ name = "Dread Spawn", spell = 144548 }}, -- During Dread Screech
+
+ --[[===========================]]
+ --[[ LATER EXPANSIONS (KEEP) ]]
+ --[[===========================]]
+
-- Raid
[167566] = {{ name = "Return to Stone", spell = 333145 }}, -- Sun King's Salvation
[165762] = {{ name = "Soul Infuser" }}, -- Sun King's Salvation
diff --git a/System/Lists/Dispels.lua b/System/Lists/Dispels.lua
index 9dec301942..9ad8f9d4af 100644
--- a/System/Lists/Dispels.lua
+++ b/System/Lists/Dispels.lua
@@ -4,6 +4,231 @@ if br.lists == nil then
end
-- Crowd Control Units = list of units to stun, either always or udner certain condition such as having a buff or wirldwinding etc
br.lists.dispell = {
+ --[[===========================]]
+ --[[ CLASSIC / VANILLA (60) ]]
+ --[[===========================]]
+
+ -- Stratholme
+ [16798] = "Magic", -- Fear (Hearthsinger Forresten)
+ [17405] = "Magic", -- Domination mind control
+
+ -- Scholomance (Vanilla)
+ [16097] = "Magic", -- Hex
+ [16799] = "Magic", -- Mind Flay
+
+ -- Blackrock Depths
+ [15063] = "Magic", -- Mind Control
+ [14875] = "Magic", -- Curse of Tongues
+
+ -- Upper Blackrock Spire
+ [15284] = "Enrage", -- Mortal Strike (bleed/enrage combo)
+ [15732] = "Magic", -- Conflagration DoT
+
+ -- Sunken Temple
+ [12480] = "Magic", -- Heal
+ [12542] = "Magic", -- Fear
+
+ -- Maraudon
+ [21869] = "Magic", -- Flame Shock / Fear
+ [21909] = "Magic", -- Toxic Volley
+
+ -- Zul'Farrak
+ [11641] = "Enrage", -- Hex
+ [11642] = "Magic", -- Heal
+
+ -- Uldaman
+ [6524] = "Magic", -- Ground Tremor
+
+ -- Molten Core
+ [19136] = "Magic", -- Stormbolt (Garr adds)
+
+ -- Blackwing Lair
+ [23339] = "Magic", -- Wing Buffet (Broodlord)
+ [24573] = "Enrage", -- Mortal Strike (Death Talon Captain)
+
+ -- Ahn'Qiraj
+ [26580] = "Magic", -- Fear (Twin Emperors)
+
+ --[[===========================]]
+ --[[ BURNING CRUSADE (70) ]]
+ --[[===========================]]
+
+ -- Shadow Labyrinth
+ [33676] = "Magic", -- Incite Chaos mind control
+ [33787] = "Magic", -- Whirlwind effect
+
+ -- Shattered Halls
+ [30636] = "Magic", -- Rend (bleed)
+ [31901] = "Magic", -- Shield Wall
+
+ -- Botanica
+ [34639] = "Magic", -- Unstable Chemicals
+ [34649] = "Magic", -- Bloodwarder Heal
+
+ -- Magisters' Terrace
+ [46757] = "Magic", -- Glyph of Power
+ [46035] = "Magic", -- Frostbolt slow
+
+ -- Mana-Tombs
+ [32860] = "Magic", -- Auchenai Soulpriest reverse healing
+ [32361] = "Magic", -- Inhibit Magic
+
+ -- Auchenai Crypts
+ [32424] = "Magic", -- Avatar of the Martyred
+
+ -- Sethekk Halls
+ [32690] = "Magic", -- Avian Ripper bleed
+ [32129] = "Magic", -- Arcane Lightning
+
+ -- Black Morass
+ [31473] = "Magic", -- Magnetic Pull
+ [33860] = "Magic", -- Heal (from Chronomancer)
+
+ -- Mechanar
+ [35159] = "Magic", -- Reflective Damage Shield
+
+ -- Blood Furnace
+ [30923] = "Magic", -- Domination mind control
+
+ -- Slave Pens
+ [31946] = "Magic", -- Corrupted Nova
+
+ -- Hellfire Ramparts
+ [30474] = "Magic", -- Burning Maul
+
+ -- Karazhan
+ [29321] = "Magic", -- Fear (Moroes adds)
+
+ -- Black Temple
+ [41338] = "Magic", -- Love Tap (Sister of Pleasure)
+
+ --[[===========================]]
+ --[[ WRATH OF THE LICH KING (80) ]]
+ --[[===========================]]
+
+ -- Violet Hold
+ [58471] = "Magic", -- Brutal Strike DoT
+
+ -- Halls of Lightning
+ [52719] = "Magic", -- Whirlwind effect
+ [52770] = "Magic", -- Defensive Stance buff
+
+ -- Culling of Stratholme
+ [52854] = "Magic", -- Banish
+ [52942] = "Magic", -- Temporal Rift slow
+
+ -- Pit of Saron
+ [69579] = "Magic", -- Puncture Wound bleed
+ [69603] = "Magic", -- Banish
+
+ -- Halls of Reflection
+ [72333] = "Magic", -- Deadly Poison
+ [72198] = "Magic", -- Remorseless Winter
+
+ -- Naxxramas
+ [28732] = "Magic", -- Curse of the Plaguebringer
+ [56153] = "Magic", -- Guardian Aura (Ahn'kahet) - Makes mobs invulnerable
+
+ -- Ulduar
+ [64320] = "Magic", -- Devouring Flame
+
+ -- Icecrown Citadel
+ [70447] = "Magic", -- Mana Barrier (Lady Deathwhisper)
+ [72257] = "Magic", -- Mark of the Fallen Champion
+
+ --[[===========================]]
+ --[[ CATACLYSM (85) ]]
+ --[[===========================]]
+
+ -- Throne of the Tides
+ [76622] = "Magic", -- Poisoned Spear
+ [76047] = "Magic", -- Water Shield
+
+ -- Vortex Pinnacle
+ [88194] = "Magic", -- Wind Blast
+ [87761] = "Magic", -- Cyclone Shield
+
+ -- Stonecore
+ [81008] = "Magic", -- Quake
+ [81637] = "Magic", -- Spinning Slash bleed
+
+ -- Grim Batol
+ [76711] = "Magic", -- Deceitful Blast
+ [76121] = "Magic", -- Warped Twilight
+
+ -- Zul'Aman (Heroic)
+ [43140] = "Magic", -- Flame Breath
+ [42471] = "Magic", -- Electrical Storm
+
+ -- Zul'Gurub (Heroic)
+ [96689] = "Magic", -- Voodoo Bolt
+ [96776] = "Magic", -- Pursuit DoT
+
+ -- Hour of Twilight
+ [103633] = "Magic", -- Backstab poison
+ [103641] = "Magic", -- Shadow Bolt
+
+ -- Firelands
+ [99516] = "Magic", -- Blazing Power
+ [99432] = "Magic", -- Fieroblast
+
+ -- Dragon Soul
+ [105479] = "Magic", -- Searing Plasma
+
+ --[[===========================]]
+ --[[ MISTS OF PANDARIA CLASSIC ]]
+ --[[===========================]]
+
+ -- Temple of the Jade Serpent
+ [106797] = "Magic", -- Hydrolance (Corrupt Living Water)
+
+ -- Stormstout Brewery
+ [114646] = "Magic", -- Haunting Gaze (Haunting Sha)
+ [106563] = "Magic", -- Carbonation (Ook-Ook)
+
+ -- Mogu'shan Palace
+ [118903] = "Magic", -- Undying Shadows
+ [119626] = "Enrage", -- Reckless Inspiration (Gekkan)
+
+ -- Shado-Pan Monastery
+ [106872] = "Magic", -- Smoke Bomb (silence)
+ [115002] = "Magic", -- Rising Hate
+
+ -- Gate of the Setting Sun
+ [115458] = "Enrage", -- Adrenaline Rush
+ [119857] = "Magic", -- Poisoned Mind
+
+ -- Siege of Niuzao Temple
+ [119365] = "Enrage", -- Enraged (Sik'thik mobs)
+
+ -- Scarlet Halls
+ [113641] = "Magic", -- Burning Hands
+
+ -- Scarlet Monastery
+ [115140] = "Enrage", -- Blazing Fists
+
+ -- Scholomance
+ [113134] = "Magic", -- Explosive Pain
+ [111631] = "Magic", -- Fixate (Books)
+
+ -- Mogu'shan Vaults (Raid)
+ [116829] = "Magic", -- Solid Stone
+
+ -- Heart of Fear (Raid)
+ [123474] = "Enrage", -- Overwhelming Assault
+
+ -- Throne of Thunder (Raid)
+ [136954] = "Enrage", -- Dino Form
+ [134378] = "Magic", -- Fixate (Ji-Kun)
+
+ -- Siege of Orgrimmar (Raid)
+ [143593] = "Magic", -- Borer Drill
+ [145071] = "Magic", -- Genetic Alteration
+
+ --[[===========================]]
+ --[[ LATER EXPANSIONS (KEEP) ]]
+ --[[===========================]]
+
--[164257] = "Enrage", -- ogres gorgrond
-- Auchindon
[160312] = "Magic",-- Void Shell
diff --git a/System/Lists/Expansions/Classic/Spells.lua b/System/Lists/Expansions/Classic/Spells.lua
new file mode 100644
index 0000000000..4205e91367
--- /dev/null
+++ b/System/Lists/Expansions/Classic/Spells.lua
@@ -0,0 +1,4215 @@
+local _, br = ...
+if br.lists == nil then br.lists = {} end
+
+-- Racial spell IDs for Classic WoW.
+br.lists.racials = {
+ NightElf = 20580, -- Shadowmeld
+ -- Dynamic lookup base IDs
+ _draeneiBase = 28880, -- Gift of the Naaru
+}
+
+br.lists.spells = {
+ DRUID = {
+ -- Initial
+ [1447] = {
+
+ },
+ -- Balance
+ [102] = {
+ abilities = {
+ celestialAlignment = 194223,
+ fullMoon = 274283,
+ furyOfElune = 202770,
+ halfMoon = 274282,
+ innervate = 29166,
+ starfire = 194153,
+ newMoon = 274281,
+ removeCorruption = 2782,
+ solarBeam = 78675,
+ typhoon = 132469,
+ wrath = 190984,
+ starfall = 191034,
+ starsurge = 78674,
+ sunfire = 93402,
+ stellarFlare = 202347,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ blessingOfElune = 202737,
+ blessingOfAnshe = 202739,
+ incarnationChoseOfElune = 102560,
+ celestialAlignment = 194223,
+ eclipse_lunar = 48518,
+ eclipse_solar = 48517,
+ onethsOverconfidence = 209407,
+ onethsIntuition = 209406,
+ solarEmpowerment = 164545,
+ innervate = 29166,
+ lunarEmpowerment = 164547,
+ warriorOfElune = 202425,
+ balanceForm = 24858,
+ stellarDrift = 202461,
+ emeraldDreamcatcher = 208190,
+ owlkinFrenzy = 157228,
+ powerOfEluneTheMoonGoddness = 208284,
+ sephuzSecret = 208052,
+ astralAcceleration = 242232,
+ newMoonController = -2027671,
+ solstice = 343648,
+ starfall = 191034,
+ starLord = 279709, --backwards compatible
+ starlord = 279709,
+ livelySpirit = 279279,
+ arcanicPulsar = 287790,
+ primordialArcanicPulsar = 338668,
+ balanceOfAllThings = 339942,
+ balanceOfAllThingsNature = 339943, -- Nature Buff
+ balanceOfAllThingsArcane = 339946, -- Arcane Buff
+ timewornDreambinder = 339949,
+ kindredEmpowerment = 327022,
+ onethsClearVision = 338661,
+ ravenousFrenzy = 323546,
+
+ },
+ conduits = {
+ preciseAlignment = 340706,
+
+
+ },
+ covenants = {
+ loneEmpowerment = 338142,
+ ravenousFrenzy = 323546,
+ empowerBond = 326446,
+ },
+ debuffs = {
+ stellarFlare = 202347,
+ moonfire = 164812,
+ sunfire = 164815,
+ stellarEmpowerment = 197637,
+ },
+ glyphs = {
+ },
+ runeforges = {
+ balanceOfAllThings = 339942,
+ lycarasFleetingGlimpse = 340059,
+ primordialArcanicPulsar = 338668,
+ timewornDreambinder = 339949,
+
+ },
+ talents = {
+ aetherialKindling = 327541,
+ astralCommunion = 202359,
+ astralSmolder = 394058,
+ balanceOfAllThings = 394049,
+ bloomingInfusion = 429433,
+ bounteousBloom = 429215,
+ celestialAlignment = 194223,
+ cenariusMight = 455797,
+ controlOfTheDream = 434249,
+ convokeTheSpirits = 323764,
+ denizenOfTheDream = 394076,
+ dreamSurge = 433831,
+ durabilityOfNature = 429227,
+ earlySpring = 428937,
+ eclipse = 79577,
+ elunesGuidance = 393991,
+ expansiveness = 429399,
+ forceOfNature = 205636,
+ furyOfElune = 202770,
+ grovesInspiration = 429402,
+ harmonyOfTheGrove = 428731,
+ incarnationChosenOfElune = 394013,
+ lightOfTheSun = 202918,
+ lunarShrapnel = 393868,
+ naturesBalance = 202430,
+ naturesGrace = 393959,
+ newMoon = 274281,
+ orbitBreaker = 383197,
+ potentEnchantments = 429420,
+ powerOfGoldrinn = 394046,
+ powerOfNature = 428859,
+ powerOfTheDream = 434220,
+ protectiveGrowth = 433748,
+ radiantMoonlight = 394121,
+ rattleTheStars = 393954,
+ shootingStars = 202342,
+ solarBeam = 78675,
+ solstice = 343647,
+ soulOfTheForest = 114107,
+ starfall = 191034,
+ starlord = 202345,
+ starweaver = 393940,
+ stellarFlare = 202347,
+ sunderedFirmament = 294108,
+ treantsOfTheMoon = 428544,
+ twinMoons = 279620,
+ umbralEmbrace = 393760,
+ umbralIntensity = 383195,
+ waningTwilight = 393956,
+ warriorOfElune = 202425,
+ wildMushroom = 88747,
+ },
+ traits = {
+ arcanicPulsar = 287773,
+ dawningSun = 276152,
+ highNoon = 278505,
+ lunarShrapnel = 278507,
+ powerOfTheMoon = 273367,
+ streakingStars = 272871,
+ sunblaze = 274397,
+ }
+ },
+ -- Feral
+ [103] = {
+ abilities = {
+ -- -- adaptiveSwarmDamage = 391889, -- Not Castable but needed for In-Flight
+ -- -- adaptiveSwarmHeal = 391891, -- Not Castable but needed for In-Flight
+ -- berserk = 106951,
+ -- maim = 22570,
+ -- -- moonfireCat = 155625,
+ -- naturesSwiftness = 132158,
+ -- pounce = 9005,
+ -- ravage = 6785,
+ -- removeCorruption = 2782,
+ -- rip = 1079,
+ -- savageRoar = 52610,
+ -- shred = 5221,
+ -- skullBash = 106839,
+ -- survivalInstincts = 61336,
+ -- swipe = 62078,
+ -- thrash = 106830,
+ },
+ buffs = {
+ -- berserk = 106952,
+ -- clearcasting = 135700,
+ -- dreamOfCenarius = 145152,
+ -- feralFury = 144865,
+ -- feralRage = 146874,
+ -- incarnation = 102543,
+ -- naturesSwiftness = 132158,
+ -- predatorySwiftness = 69369,
+ -- savageRoar = 52610,
+ -- survivalInstincts = 61336,
+ },
+ debuffs = {
+ -- pounce = 9005,
+ -- rip = 1079,
+ -- thrash = 106830,
+ },
+ items = {
+ },
+ talents = {
+ -- cenarionWard = 102351,
+ -- disorientingRoar = 99,
+ -- displacerBeast = 102280,
+ -- dreamOfCenarius = 108373,
+ -- faerieSwarm = 106707,
+ -- felineSwiftness = 131768,
+ -- forceOfNature = 106737,
+ -- heartOfTheWild = 108288,
+ -- incarnation = 106731,
+ -- massEntanglement = 102359,
+ -- mightyBash = 5211,
+ -- naturesVigil = 124974,
+ -- renewal = 108238,
+ -- soulOfTheForest = 114107,
+ -- typhoon = 132469,
+ -- ursolsVortex = 102793,
+ -- wildCharge = 102401,
+ -- yserasGift = 145108,
+ },
+ },
+ -- Guardian
+ [104] = {
+ abilities = {
+ berserk = 50334,
+ incapacitatingRoar = 99,
+ incarnationGuardianOfUrsoc = 102558,
+ maul = 6807,
+ removeCorruption = 2782,
+ skullBash = 106839,
+ stampedingRoar = 106898,
+ survivalInstincts = 61336,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ berserk = 50334,
+ galacticGuardian = 213708,
+ goryFur = 201671,
+ incarnationGuardianOfUrsoc = 102558,
+ gore = 93622,
+ pulverize = 158792,
+ savageCombatant = 340613,
+ survivalInstincts = 61336,
+ toothAndClaw = 135286,
+ },
+ conduits = {
+ savageCombatant = 340609,
+
+ },
+ covenants = {
+ loneProtection = 338018,
+ },
+ debuffs = {
+ moonfireGuardian = 164812,
+ toothAndClaw = 135601,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ afterTheWildfire = 371905,
+ aggravateWounds = 441829,
+ berserkPersistence = 377779,
+ berserkRavage = 343240,
+ berserkUncheckedAggression = 377623,
+ bestialStrength = 441841,
+ bloodFrenzy = 203962,
+ brambles = 203953,
+ bristlingFur = 155835,
+ circleOfLifeAndDeath = 391969,
+ clawRampage = 441835,
+ convokeTheSpirits = 323764,
+ dreadfulWound = 441809,
+ dreamOfCenarius = 372119,
+ earthwarden = 203974,
+ elunesFavored = 370586,
+ empoweredShapeshifting = 441689,
+ flashingClaws = 393427,
+ fountOfStrength = 441675,
+ -- frontOfThePack = 377835,
+ furyOfNature = 370695,
+ galacticGuardian = 203964,
+ gore = 210706,
+ goryFur = 200854,
+ guardianOfElune = 213680,
+ improvedSurvivalInstincts = 328767,
+ incarnationGuardianOfUrsoc = 102558,
+ infectedWounds = 345208,
+ innateResolve = 377811,
+ killingStrikes = 441824,
+ layeredMane = 279552,
+ mangle = 231064,
+ maul = 6807,
+ packsEndurance = 441844,
+ pulverize = 80313,
+ rageOfTheSleeper = 200851,
+ ravage = 441583,
+ reinforcedFur = 393618,
+ reinvigoration = 372945,
+ rendAndTear = 204053,
+ ruthlessAggression = 441814,
+ scintillatingMoonlight = 238049,
+ soulOfTheForest = 158477,
+ strikeForTheHeart = 441845,
+ survivalInstincts = 61336,
+ survivalOfTheFittest = 203965,
+ tearDownTheMighty = 441846,
+ toothAndClaw = 135288,
+ twinMoonfire = 372567,
+ untamedSavagery = 372943,
+ -- ursineAdept = 300346,
+ ursocsEndurance = 393611,
+ ursocsFury = 372505,
+ ursocsGuidance = 393414,
+ viciousCycle = 371999,
+ vulnerableFlesh = 372618,
+ wildpowerSurge = 441691,
+ wildshapeMastery = 441678,
+ },
+ traits = {
+ layeredMane = 279552,
+ twistedClaws = 275906,
+ },
+ },
+ -- Restoration
+ [105] = {
+ abilities = {
+ efflorescence = 145205,
+ innervate = 29166,
+ ironbark = 102342,
+ lifebloom = 33763,
+ naturesCure = 88423,
+ nourish = 50464,
+ revitalize = 212040,
+ wrath = 5176,
+ sunfire = 93402,
+ tranquility = 740,
+ typhoon = 132469,
+ yserasGift = 145108,
+ naturesSwiftness = 132158,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ abundance = 207640,
+ cenarionWard = 102352,
+ clearcasting = 16870,
+ incarnationTreeOfLife = 33891,
+ innervate = 29166,
+ groveTending = 279793,
+ rejuvenationGermination = 155777,
+ lifebloom = 33763,
+ eclipse_lunar = 48518,
+ eclipse_solar = 48517,
+ --lunarEmpowerment = 164547,
+ regrowth = 8936,
+ --solarEmpowerment = 164545,
+ soulOfTheForest = 114108,
+ cultivat = 200389,
+ tranquility = 157982,
+ springblossom = 207386,
+ symbolOfHope = 64901,
+ fullbloom = 290213,
+ heartOfTheWild = 108291,
+ },
+ conduits = {
+ loneMeditation = 338035,
+ },
+ debuffs = {
+ moonfire = 164812,
+ sunfire = 164815,
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+ theDarkTitansLesson = 338831,
+ verdantInfusion = 338829,
+
+ },
+ talents = {
+ abundance = 207383,
+ balanceAffinity = 197632,
+ bloomingInfusion = 429433,
+ bondWithNature = 439929,
+ bounteousBloom = 429215,
+ buddingLeaves = 392167,
+ burstingGrowth = 440120,
+ callOfTheElderDruid = 426784,
+ cenarionWard = 102351,
+ cenariusGuidance = 393371,
+ cenariusMight = 455797,
+ controlOfTheDream = 434249,
+ cultivation = 200390,
+ dreamSurge = 433831,
+ durabilityOfNature = 429227,
+ earlySpring = 428937,
+ entanglingVortex = 439895,
+ expansiveness = 429399,
+ feralAffinity = 197490,
+ flourish = 197721,
+ flowerWalk = 439901,
+ germination = 155675,
+ grovesInspiration = 429402,
+ guardianAffinity = 197491,
+ harmoniousConstitution = 440116,
+ harmonyOfTheGrove = 428731,
+ huntBeneathTheOpenSkies = 439868,
+ implant = 440118,
+ incarnationTreeOfLife = 33891,
+ innerPeace = 197073,
+ lethalPreservation = 455461,
+ nourish = 50464,
+ overgrowth = 203651,
+ photosynthesis = 274902,
+ potentEnchantments = 429420,
+ powerOfNature = 428859,
+ powerOfTheDream = 434220,
+ protectiveGrowth = 433748,
+ resilientFlourishing = 439880,
+ rootNetwork = 439882,
+ soulOfTheForest = 158478,
+ springBlossoms = 207385,
+ strategicInfusion = 439890,
+ thrivingGrowth = 439528,
+ treantsOfTheMoon = 428544,
+ twinSprouts = 440117,
+ vigorousCreepers = 440119,
+ wildstalkersPower = 439926,
+ },
+ traits = {
+ dawningSun = 276152,
+ highNoon = 278505,
+ }
+ },
+ -- All
+ Shared = {
+ abilities = {
+ -- Bear Form
+ bash = {5211, 6798, 8983}, -- Ranks 1-3
+ demoralizingRoar = 99,
+ enrage = 5229,
+ -- mangleBear = 33878, -- TBC only
+ maul = {6807, 6808, 6809, 8972, 9745, 9880, 9881}, -- Ranks 1-7
+ swipe = {779, 780, 769, 9754}, -- Ranks 1-4
+ -- Cat Form
+ claw = {1082, 3029, 5201, 9849, 9850, 9851}, -- Ranks 1-6
+ faerieFireFeral = 16857,
+ ferociousBite = {22568, 22827, 22828, 22829}, -- Ranks 1-4
+ pounce = 9005,
+ prowl = 5215,
+ rake = {1822, 1823, 1824, 9904, 9845}, -- Ranks 1-5
+ ravage = 6785,
+ rip = {1079, 9492, 9493, 9752, 9894, 9896}, -- Ranks 1-6
+ shred = {5221, 6800, 8992, 9829, 9830}, -- Ranks 1-5
+ tigersFury = 5217,
+ -- Caster Form
+ barkskin = 22812,
+ entanglingRoots = 339,
+ faerieFire = 770,
+ healingTouch = {5185, 5186, 5187, 5188, 5189, 6778, 8903, 9758, 9888, 9889}, -- Ranks 1-10
+ innervate = 29166,
+ markOfTheWild = {1126, 5232, 6756, 5234, 8907, 9884, 9885}, -- Ranks 1-7
+ moonfire = {8921, 8924, 8925, 8926, 8927, 8928, 8929, 9833, 9834, 9835}, -- Ranks 1-10
+ naturesGrasp = 16689,
+ omenOfClarity = 16864,
+ regrowth = {8936, 8938, 8939, 8940, 8941, 9750, 9856, 9857, 9858}, -- Ranks 1-9
+ rejuvenation = {774, 1058, 1430, 2090, 2091, 3627, 8910, 9839, 9840, 9841}, -- Ranks 1-10
+ starfire = {2912, 8949, 8950, 8951, 9875, 9876}, -- Ranks 1-6
+ thorns = {467, 782, 1075, 8914, 9756, 9910}, -- Ranks 1-6
+ wrath = {5176, 5177, 5178, 5179, 5180, 6780, 8905, 9912}, -- Ranks 1-8
+ -- Dispels
+ abolishPoison = 2893,
+ curePoison = 8946,
+ removeCurse = 2782,
+ -- General
+ autoAttack = 6603,
+ -- Shapeshifting
+ aquaticForm = 1066,
+ bearForm = 5487,
+ catForm = 768,
+ direBearForm = 9634,
+ moonkinForm = 24858,
+ travelForm = 783,
+ },
+ animas = {
+ -- lycarasBargin = 329960,
+ -- lycarasTwig = 330666,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ -- Buffs
+ abolishPoison = 2893,
+ barkskin = 22812,
+ clearcasting = 16870,
+ enrage = 5229,
+ markOfTheWild = {1126, 5232, 6756, 5234, 8907, 9884, 9885},
+ naturesGrasp = 16689,
+ omenOfClarity = 16864,
+ prowl = 5215,
+ regrowth = {8936, 8938, 8939, 8940, 8941, 9750, 9856, 9857, 9858},
+ rejuvenation = {774, 1058, 1430, 2090, 2091, 3627, 8910, 9839, 9840, 9841},
+ shadowmeld = 20580,
+ thorns = {467, 782, 1075, 8914, 9756, 9910},
+ tigersFury = 5217,
+ -- Forms
+ aquaticForm = 1066,
+ bearForm = 5487,
+ catForm = 768,
+ direBearForm = 9634,
+ moonkinForm = 24858,
+ travelForm = 783,
+ },
+ conduits = {
+ -- deepAllegiance = 341378,
+ -- tasteForBlood = 340682,
+ },
+ covenants = {
+ -- adaptiveSwarm = 325727,
+ -- adaptiveSwarmHeal = 325748,
+ -- convokeTheSpiritsCovenant = 323764,
+ -- kindredSpirits = 326434,
+ -- ravenousFrenzy = 323546,
+ },
+ debuffs = {
+ demoralizingRoar = 99,
+ entanglingRoots = 339,
+ faerieFire = {770, 778, 9749, 9907},
+ faerieFireFeral = {16857, 17390, 17391, 17392},
+ moonfire = {8921, 8924, 8925, 8926, 8927, 8928, 8929, 9833, 9834, 9835},
+ rake = {1822, 1823, 1824, 9904},
+ rip = {1079, 9492, 9493, 9752, 9894, 9896},
+ },
+ glyphs = {
+ -- glyphOfTheCheetah = 131113,
+ -- glyphOfTheDoe = 224122,
+ -- glyphOfTheFeralChameleon = 210333,
+ -- glyphOfTheOrca = 114333,
+ -- glyphOfTheSentinel = 219062,
+ -- glyphOfTheUrsolChameleon = 107059,
+ },
+ runeforges = {
+ -- apexPredatorsCraving = 339139,
+ -- eyeOfFearfullSymmetry = 339141,
+ -- frenzyband = 340053,
+ },
+ talents = {
+ },
+ talentsHeroic = {
+ -- bloomingInfusion = 429433,
+ -- bounteousBloom = 429215,
+ -- burstingGrowth = 440120,
+ -- cenariusMight = 455797,
+ -- controlOfTheDream = 434249,
+ -- dreamSurge = 433831,
+ -- durabilityOfNature = 429227,
+ -- earlySpring = 428937,
+ -- expansiveness = 429399,
+ -- grovesInspiration = 429402,
+ -- harmonyOfTheGrove = 428731,
+ -- potentEnchantments = 429420,
+ -- powerOfNature = 428859,
+ -- powerOfTheDream = 434220,
+ -- protectiveGrowth = 433748,
+ -- resilientFlourishing = 439880,
+ -- rootNetwork = 439882,
+ -- treantsOfTheMoon = 428544,
+ },
+ traits = {
+ -- livelySpirit = 279642,
+ },
+ },
+ },
+ HUNTER = {
+ -- BeastMastery
+ [253] = {
+ abilities = {
+ aspectOfTheWild = 193530,
+ barbedShot = 217200,
+ bestialWrath = 19574,
+ cobraShot = 193455,
+ concussiveShot = 5116,
+ counterShot = 147362,
+ intimidation = 19577,
+ killCommand = 34026,
+ killShot = 53351,
+ multishot = 2643,
+ },
+ animas = {
+ piercingScope = 331183,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ aspectOfTheWild = 193530,
+ barbedShot = 217200,
+ beastCleave = 118455,
+ berserking = 26297,
+ bestialWrath = 19574,
+ danceOfDeath = 274443,
+ direBeast = 120694,
+ frenzy = 272790,
+ parselsTongue = 248084,
+ spittingCobra = 194407,
+ volley = 194386,
+ },
+ debuffs = {
+ barbedShot = 217200,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ animalCompanion = 267116,
+ barrage = 120360,
+ bloodshed = 321530,
+ direBeast = 120679,
+ killCleave = 378207,
+ killerCobra = 199532,
+ killerInstinct = 273887,
+ oneWithThePack = 199528,
+ scentOfBlood = 193532,
+ stampede = 201430,
+ stomp = 199530,
+ thrillOfTheHunt = 257944,
+ wailingArrow = 392060,
+ },
+ traits = {
+ danceOfDeath = 274441,
+ primalInstincts = 279806,
+ rapidReload = 278530,
+ },
+ },
+ -- Marksmanship
+ [254] = {
+ abilities = {
+ },
+ artifacts = {
+ },
+ buffs = {
+ bulletstorm = 389020,
+ bullseye = 204090,
+ deadEye = 321461,
+ deathblow = 378770,
+ feignDeath = 5384,
+ inTheRhythm = 272733,
+ lethalShots = 260395,
+ lockAndLoad = 194594,
+ masterMarksman = 269576,
+ preciseShots = 260242,
+ razorFragments = 388993,
+ salvo = 400456,
+ steadyFocus = 193534,
+ trickShots = 257622,
+ trueshot = 288613,
+ unerringVision = 274447,
+ },
+ debuffs = {
+ huntersMark = 257284,
+ serpentSting = 271788,
+ },
+ glyphs = {
+
+ },
+ interrupts = {
+ counterShot = 147362,
+ },
+ talents = {
+ aimedShot = 19434,
+ barrage = 120360,
+ bindingShackles = 321468,
+ bulletstorm = 389019,
+ bullseye = 204089,
+ callingTheShots = 260404,
+ carefulAim = 260228,
+ chimaeraShot = 342049,
+ crackShot = 321293,
+ deathblow = 378769,
+ eagletalonsTrueFocus = 389449,
+ explosiveShot = 212431,
+ fanTheHammer = 459794,
+ focusedAim = 378767,
+ heavyAmmo = 378910,
+ hydrasBite = 260241,
+ improvedSteadyShot = 321018,
+ inTheRhythm = 407404,
+ killShot = 53351,
+ killZone = 459921,
+ killerAccuracy = 378765,
+ legacyOfTheWindrunners = 406425,
+ lightAmmo = 378913,
+ lockAndLoad = 194595,
+ loneWolf = 155228,
+ masterMarksman = 260309,
+ multishot = 257620,
+ nightHunter = 378766,
+ penetratingShots = 459783,
+ preciseShots = 260240,
+ rapidFire = 257044,
+ rapidFireBarrage = 459800,
+ razorFragments = 384790,
+ readiness = 389865,
+ salvo = 400456,
+ serpentstalkersTrickery = 378888,
+ smallGameHunter = 459802,
+ steadyFocus = 193533,
+ streamline = 260367,
+ surgingShots = 391559,
+ tacticalReload = 400472,
+ trickShots = 257621,
+ trueshot = 288613,
+ unerringVision = 386878,
+ volley = 260243,
+ wailingArrow = 459806,
+ },
+ traits = {
+ focusedFire = 278531,
+ inTheRhythm = 264198,
+ rapidReload = 278530,
+ steadyAim = 277651,
+ surgingShots = 287707,
+ unerringVision = 274444,
+ },
+ },
+ -- Survival
+ [255] = {
+ abilities = {
+ aspectOfTheEagle = 186289,
+ carve = 187708,
+ coordinatedAssault = 266779,
+ harpoon = 190925,
+ intimidation = 19577,
+ killCommand = 259489,
+ killShot = 320976,
+ muzzle = 187707,
+ pheromoneBomb = 270323, -- Wildfire Infusion
+ raptorStrike = 186270,
+ serpentSting = 259491,
+ shrapnelBomb = 270335, -- Wildfire Infusion
+ volatileBomb = 271045, -- Wildfire Infusion
+ wakeUp = 210000,
+ wildfireBomb = 259495,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ aspectOfTheEagle = 186289,
+ blurOfTalons = 277969,
+ coordinatedAssault = 360952,
+ exposedFlank = 252094, -- Tier 21
+ mongooseFury = 259388,
+ tipOfTheSpear = 260286,
+ vipersVenom = 268552,
+ madBombardier = 363805, -- Tier 28
+ },
+ debuffs = {
+ bloodseeker = 259277,
+ internalBleeding = 270343,
+ serpentSting = 259491,
+ shrapnelBomb = 270339,
+ wildfireBomb = 269747,
+ pheromoneBomb = 270332,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ alphaPredator = 269737,
+ birdsOfPrey = 260331,
+ bloodseeker = 260248,
+ butchery = 212436,
+ crackShot = 321293,
+ flankingStrike = 269751,
+ furyOfTheEagle = 203415,
+ guerrillaTactics = 264332,
+ hydrasBite = 260241,
+ mongooseBite = 259387,
+ steelTrap = 162488,
+ termsOfEngagement = 265895,
+ tipOfTheSpear = 260285,
+ vipersVenom = 268501,
+ wildfireInfusion = 271014,
+ },
+ traits = {
+ blurOfTalons = 277653,
+ latentPoison = 273283,
+ primevalIntuition = 288570,
+ upCloseAndPersonal = 278533,
+ venomousFangs = 274590,
+ wildernessSurvival = 279589,
+ }
+ },
+ -- All
+ Shared = {
+ abilities = {
+ -- Hunter Abilities
+ arcaneShot = 185358,
+ aspectOfTheChameleon = 61648,
+ aspectOfTheCheetah = 186257,
+ aspectOfTheTurtle = 186265,
+ disengage = 781,
+ eagleEye = 6197,
+ exhilaration = 109304,
+ eyeOfTheBeast = 321297,
+ feignDeath = 5384,
+ flare = 1543,
+ fortitudeOfTheBear = 392956,
+ freezingTrap = 187650,
+ huntersMark = 257284,
+ steadyShot = 56641,
+ trackBeasts = 1494,
+ trackDemons = 19878,
+ trackDragonkin = 19879,
+ trackElementals = 19880,
+ trackGiants = 19882,
+ trackHidden = 19885,
+ trackHumanoids = 19883,
+ trackMechanicals = 229533,
+ trackUndead = 19884,
+ wingClip = 195645,
+ -- Pet Management
+ beastLore = 1462,
+ callPet = 9,
+ callPet1 = 883,
+ callPet2 = 83242,
+ callPet3 = 83243,
+ callPet4 = 83244,
+ callPet5 = 83245,
+ commandPet = 272651,
+ dismissPet = 2641,
+ feedPet = 6991,
+ fetch = 125050,
+ mendPet = 136,
+ playDead = 209997,
+ revivePet = 982,
+ tameBeast = 1515,
+ wakeUp = 210000,
+ ----------------------------
+ -- Pet - Basic Abilities ---
+ ----------------------------
+ bite = 17253, -- Basalisk, Bat, Beetle, Blood Beast, Boar, Carapid, Carrion Bird, Chimera, Core Hound, Crocolisk, Devilsaur, Direhorn, Dragonhawk, Feathermane, Fox, Hound, Hydra, Hyena, Lizard, Mechanical, Pterrordax, Ravager, Ray, Riverbeast, Serpent, Shale Beast, Spider, Stone Hound, Turtle, Warp Stalker, Water Strider, Wind Serpent, Wolf, Worm
+ claw = 16827, -- Aqiri, Bear, Bird of Prey, Cat, Crab, Raptor, Rodent, Scorpid, Spirit Beast, Tallstrider
+ dash = 61684, -- Speed Increase (ALL PETS)
+ smack = 49966, -- Camel, Clefthoof, Courser, Crane, Gorilla, Gruffhorn, Mammoth, Monkey, Moth, Oxen, Scalehide, Sporebat, Stag, Toad, Wasp
+ growl = 2649, -- Taunt (ALL PETS)
+ ---------------------------
+ -- Pet - Spec Abilities ---
+ ---------------------------
+ primalRage = 264667, -- Ferocity Dmg Cd
+ -- survivalOfTheFittest = 264735, -- Tenacity Def Cd
+ mastersCall = 53271, -- Cunning Freedom
+ ------------------------------
+ -- Pet - Special Abilities ---
+ ------------------------------
+ -- AOE
+ burrowAttack = 93433, -- Worm
+ froststormBreath = 92380, -- Chimera - Channeled Cone AOE
+ -- Defense/Dodge
+ agileReflexes = 160011, -- Fox - Dodge
+ bristle = 263869, -- Boar - Defensive
+ bulwark = 279410, -- Carapid - Defensive
+ catlikeReflexes = 263892, -- Cat - Dodge
+ defenseMatrix = 263868, -- Mechanical - Defensive
+ dragonsGuile = 263887, -- Dragonhawk - Dodge
+ featherFlurry = 263916, -- Feathermane - Dodge
+ fleethoof = 341117, -- Courser - Dodge
+ hardenCarapace = 90339, -- Beetle - Defensive
+ obsidianSkin = 263867, -- Core Hound - Defensive
+ primalAgility = 160044, -- Monkey - Dodge
+ scaleShield = 263865, -- Scalehide - Defensive
+ serpentSwiftness = 263904, -- Serpent - Dodge
+ shellShield = 26064, -- Turtle - Defensive
+ solidShell = 160063, -- Shale Beast - Defensive
+ swarmOfFlies = 279336, -- Toad - Dodge
+ wingedAgility = 264360, -- Wind Serpent - Dodge
+ -- Dispel
+ chiJiTranq = 344350, -- Crane
+ naturesGrace = 344352, -- Stag
+ netherEnergy = 344349, -- Ray -- netherEnergy
+ serenityDust = 344353, -- Moth
+ sonicScreech = 344348, -- Bat -- sonicScreech
+ soothingWater = 344346, -- Water Strider
+ spiritPulse = 344351, -- Spirit Beast -- spiritPulse
+ sporeCloud = 344347, -- Sporebat
+ -- Heal
+ eternalGuardian = 267922, -- Stone Hound - Ressurect
+ feast = 159953, -- Devilsaur - Heal (req dead humanoid/beast nearby)
+ spiritmend = 90361, -- Spirit Beast - Heal
+ -- Mortal Wounds Debuff
+ acidBite = 263863, -- Hydra - Mortal Wounds Debuff
+ bloodyScreech = 24423, -- Carrion Bird - Mortal Wounds Debuff
+ deadlySting = 160060, -- Scorpid - Mortal Wounds Debuff
+ gnaw = 263856, -- Rodent - Mortal Wounds Debuff
+ gore = 263861, -- Direhorn - Mortal Wounds Debuff
+ grievousBite = 279362, -- Lizard - Mortal Wounds Debuff
+ gruesomeBite = 160018, -- Riverbeast - Mortal Wounds Debuff
+ infectedBite = 263853, -- Hyena - Mortal Wounds Debuff
+ monsterousBite = 54680, -- Devilsaur - Mortal Wounds Debuff
+ ravage = 263857, -- Ravager - Mortal Wounds Debuff
+ savageRend = 263854, -- Savage Rend - Mortal Wounds Debuff
+ toxicSting = 263858, -- Wasp - Mortal Wounds Debuff
+ -- Slow
+ acidSpit = 263446, -- Worm - Slow
+ ankleCrack = 50433, -- Crocolisk - Slow
+ bloodBolt = 288962, -- Blood Beast - Slow
+ dustCloud = 50285, -- Tallstrider - Slow
+ frostBreath = 54644, -- Chimera - Slow
+ furiousBite = 263840, -- Wolf - Slow
+ lockJaw = 263423, -- Hound - Slow
+ petrifyingGaze = 263841, -- Basilisk - Slow
+ pin = 50245, -- Crab - Slow
+ talonRend = 263852, -- Bird of Prey - Slow
+ tendonRip = 160065, -- Aqiri - Slow
+ trample = 341118, -- Mammoth - Slow
+ warpTime = 35346, -- Warp Stalker - Slow
+ webSpray = 160067, -- Spider - Slow
+ -- Slow Fall
+ updraft = 160007, -- Feathermane/Pterrordax - Slow Fall
+ -- Stealth
+ prowl = 24450, -- Stealth
+ spiritWalk = 90328, -- Spirit Beast - Stealth
+ -- Tricks / Play / Rest
+ play = 90347, -- Fox
+ restBear = 94019, -- Bear
+ restRodent = 126364, -- Rodent
+ trickCrane = 126259, -- Crane
+ trickBirdOfPrey = 94022, -- Bird of Prey
+ -- Water Walking
+ surfaceTrot = 126311, -- Water Strider - Water Walking
+ },
+ animas = {
+ soulforgeEmbers = 331197,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ aspectOfTheTurtle = 186265,
+ eagletalonsTrueFocus = 336851,
+ feignDeath = 5384,
+ flayersMark = 324156, -- Covenant
+ killingFrenzy = 363665, -- T28 4pc
+ mendPet = 136,
+ nesingwarysTrappingApparatus = 336744,
+ playDead = 209997,
+ prowl = 24450,
+ resonatingArrow = 356263, -- Covenant
+ spiritWalk = 90328,
+ surfaceTrot = 126311, -- Water Strider - Water Walking
+ updraft = 160007, -- Feathermane/Pterrordax - Slow Fall
+ volley = 260243,
+ wildSpirits = 328231,
+ },
+ conduits = {
+ bloodletting = 341440,
+ marksmansAdvantage = 339264,
+ },
+ covenants = {
+ flayedShot = 324149,
+ resonatingArrow = 308491,
+ wildSpirits = 328231,
+ },
+ debuffs = {
+ bestialFerocity = 191413,
+ freezingTrap = 3355,
+ huntersMark = 257284,
+ intimidation = 24934,
+ latentPoison = 378015,
+ mortalWounds = 115804, -- Pet Ability Debuff
+ resonatingArrow = 308498, -- Covenant
+ soulforgeEmbers = 331269, -- Covenant
+ tarTrap = 135299,
+ wildMark = 328275, -- Covenant
+ },
+ glyphs = {
+
+ },
+ interrupts = {
+ freezingTrap = 187650,
+ },
+ runeforges = {
+ bagOfMunitions = 356264,
+ eagletalonsTrueFocus = 336849,
+ nesingwarysTrappingApparatus = 336743,
+ pouchOfRazorFragments = 356618,
+ rylakstalkersConfoundingStrikes = 336901,
+ secretsOfTheUnblinkingVigil = 336878,
+ serpentstalkersTrickery = 336870,
+ soulforgeEmbers = 336745,
+ surgingShots = 336867,
+ qaplaEredunWarOrder = 336830,
+ },
+ talents = {
+ bindingShot = 109248,
+ blackrockMunitions = 462036,
+ bornToBeWild = 266921,
+ burstingShot = 186387,
+ camouflage = 199483,
+ concussiveShot = 5116,
+ counterShot = 147362,
+ devilsaurTranquilizer = 459991,
+ disruptiveRounds = 343244,
+ emergencySalve = 459517,
+ entrapment = 393344,
+ ghillieSuit = 459466,
+ highExplosiveTrap = 236776,
+ huntersAvoidance = 384799,
+ implosiveTrap = 462031,
+ improvedKillShot = 343248,
+ improvedTraps = 343247,
+ intimidation = 19577,
+ keenEyesight = 378004,
+ kindlingFlare = 459506,
+ kodoTranquilizer = 459983,
+ loneSurvivor = 388039,
+ misdirection = 34477,
+ momentOfOpportunity = 459488,
+ naturalMending = 270581,
+ noHardFeelings = 459546,
+ paddedArmor = 459450,
+ pathfinding = 378002,
+ posthaste = 109215,
+ quickLoad = 378771,
+ rejuvenatingWind = 385539,
+ roarOfSacrifice = 53480,
+ scareBeast = 1513,
+ scatterShot = 213691,
+ scoutsInstincts = 459455,
+ scrappy = 459533,
+ serratedTips = 459502,
+ survivalOfTheFittest = 264735,
+ specializedArsenal = 459542,
+ tarTrap = 187698,
+ tarcoatedBindings = 459460,
+ territorialInstincts = 459507,
+ trailblazer = 199921,
+ tranquilizingShot = 19801,
+ triggerFinger = 459534,
+ unnaturalCauses = 459527,
+ wildernessMedicine = 343242,
+ },
+ talentsHeroic = {
+ -- Dark Ranger
+ blackArrow = 430703,
+ darkChains = 430712,
+ darkEmpowerment = 430718,
+ deathShade = 430711,
+ darknessCalls = 430722,
+ embraceTheShadows = 430704,
+ graveReaper = 430719,
+ overshadow = 430716,
+ shadowErasure = 430720,
+ shadowHounds = 430707,
+ shadowLash = 430717,
+ shadowSurge = 430714,
+ smokeScreen = 430709,
+ witheringFire = 430715,
+ -- Pack Leader
+ beastOfOpportunity = 445700,
+ corneredPrey = 445702,
+ coveringFire = 445715,
+ cullTheHerd = 445717,
+ denRecovery = 445710,
+ frenziedTear = 445696,
+ furiousAssault = 445699,
+ howlOfThePack = 445707,
+ packAssault = 445721,
+ packCoordination = 445505,
+ scatteredPrey = 445768,
+ tirelessHunt = 445701,
+ viciousHunt = 445404,
+ wildAttacks = 445708,
+ -- Sentinel
+ catchOut = 451516,
+ crescentSteel = 451530,
+ extrapolatedShots = 450374,
+ eyesClosed = 450381,
+ dontLookBack = 450373,
+ invigoratingPulse = 450379,
+ lunarStorm = 450385,
+ overwatch = 450384,
+ releaseAndReload = 450376,
+ sentinel = 450369,
+ sentinelPrecision = 450375,
+ sentinelWatch = 451546,
+ sideline = 450378,
+ symphonicArsenal = 450383,
+ },
+ },
+ },
+ MAGE = {
+ -- Arcane
+ [62] = {
+ abilities = {
+ alterTime = 342245,
+ arcaneBarrage = 44425,
+ arcaneBlast = 30451,
+ arcaneFamiliar = 205022,
+ arcaneMissiles = 5143,
+ arcaneOrb = 153626,
+ arcanePower = 12042,
+ chargedUp = 205032,
+ conjuremanaGem = 759,
+ displacement = 212801,
+ erosion = 205039,
+ evocation = 12051,
+ -- fireblast = 519836,
+ greaterInvisibility = 110959,
+ markOfAluneth = 224968,
+ netherTempest = 114923,
+ presenceofMind = 205025,
+ prismaticBarrier = 235450,
+ runeofPower = 116011,
+ slow = 31589,
+ supernova = 157980,
+ touchOfTheMagi = 321507,
+ },
+ artifacts = {
+ aegwynnsAscendance = 187680,
+ aegwynnsFury = 187287,
+ aegwynnsInperative = 187264,
+ aegwynnsIntensity = 238054,
+ aegwynnsWrath = 187321,
+ alunethsAvarice = 238090,
+ ancientPower = 214917,
+ arcanePurification = 187313,
+ arcaneRebound = 188006,
+ blastingRod = 187258,
+ cracklingEnergy = 187310,
+ etherealSensitivity = 187276,
+ everywhereAtOnce = 187301,
+ forceBarrier = 210716,
+ intensityOfTheTirisgarde = 241121,
+ markOfAluneth = 224968,
+ mightOfTheGuardians = 187318,
+ ruleOfThrees = 215463,
+ sloooowDown = 210730,
+ timeAndSpace = 238126,
+ torrentialBarrage = 187304,
+ touchOfTheMagi = 210725,
+ },
+ buffs = {
+ arcaneCharge = 36032,
+ arcaneFamiliar = 210126,
+ arcanePower = 12042,
+ -- arcaneCharge = 36032,
+ evocation = 12051,
+ expandedPotential = 327495,
+ arcaneMissles = 79683,
+ presenceOfMind = 205025,
+ prismaticBarrier = 235450,
+ rhoninsAssaultingArmwraps = 208081,
+ runeofPower = 116011,
+ ruleOfThrees = 264774,
+ brainStorm = 273330,
+ clearcasting = 263725,
+ },
+ debuffs = {
+ arcaneCharge = 36032,
+ netherTempest = 114923,
+ touchoftheMagi = 210824,
+
+
+ },
+ glyphs = {
+
+ },
+ talents = {
+ arcaneEcho = 342231,
+ amplification = 236628,
+ arcaneFamiliar = 205022,
+ arcaneOrb = 153626,
+ chronoShift = 235711,
+ mastersOfTime = 342249,
+ --erosion = 205039,
+ netherTempest = 114923,
+ overpowered = 155147,
+ resonance = 205028,
+ reverberate = 281482,
+ rulesOfThrees = 264354,
+ slipstream = 236457,
+ supernova = 157980,
+ timeAnomaly = 210805
+ --temporalFlux = 234302,
+ --wordsOfPower = 205035,
+ },
+ traits = {
+ anomalousImpact = 279867,
+ arcanePressure = 274594,
+ arcanePummeling = 270669,
+ brainStorm = 273326,
+ explosiveEcho = 278537,
+ galvanizingSpark = 278536,
+ },
+ },
+ -- Fire
+ [63] = {
+ abilities = {
+ alterTime = 108978,
+ blazingBarrier = 235313,
+ blastWave = 157981,
+ cinderstorm = 198929,
+ combustion = 190319,
+ dragonsBreath = 31661,
+ disciplinaryCommand = 327365,
+ fireball = 133,
+ fireBlast = 108853,
+ fireBlast2 = 319836,
+ flameOn = 205029,
+ flamestrike = 2120,
+ infernalCascade = 336821,
+ livingBomb = 44457,
+ meteor = 153561,
+ phoenixFlames = 257541,
+ pyroblast = 11366,
+ shiftingPower = 314791,
+ scorch = 2948,
+ soulIgnition = 345211,
+ },
+ artifacts = {
+ aftershocks = 194431,
+ phoenixReborn = 215773,
+ },
+ buffs = {
+ blazingBarrier = 235313,
+ blasterMaster = 274598,
+ combustion = 190319,
+ disciplinaryArcane = 327369,
+ disciplinaryCommand = 327365,
+ disciplinaryCommandFrost = 327366,
+ disciplinaryCommandFire = 327368,
+ firestorm = 333097,
+ heatingUp = 48107,
+ hotStreak = 48108,
+ iceFloes = 108839,
+ infernalCascade = 336832,
+ kaelthasUltimateAbility = 209455,
+ preheat = 273333,
+ pyroclasm = 269651,
+ shiftingPower = 314791,
+ sunKingsBlessings = 333313,
+ soulIgnition = 345211,
+ },
+ debuffs = {
+ ignite = 12654,
+ meteorBurn = 155158,
+ cauterized = 87024,
+
+ },
+ glyphs = {
+
+ },
+ talents = {
+ alexstraszasFury = 235870,
+ blastWave = 157981,
+ blazingSoul = 235365,
+ --cinderstorm = 198929,
+ conflagration = 205023,
+ --controlledBurn = 205033,
+ freneticSpeed = 236058,
+ firestarter = 205026,
+ flameOn = 205029,
+ flamePatch = 205037,
+ kindling = 155148,
+ livingBomb = 44457,
+ meteor = 153561,
+ focusMagic = 321358,
+ fromTheAshes = 342344,
+ pyromaniac = 205020,
+ pyroclasm = 269650,
+ searingTouch = 269644,
+ },
+ covenants = {
+ shiftingPower = 314791,
+ },
+ traits = {
+ preheat = 273332,
+ blasterMaster = 274596,
+ },
+ conduits = {
+ infernalCascade = 336821,
+ },
+ runeforges = {
+ firestorm = 333097,
+ grislyIcicle = 333393,
+ disciplinaryCommand = 327365,
+ sunKingsBlessings = 333313
+ }
+ },
+ -- Frost
+ [64] = {
+ abilities = {
+ alterTime = 108978,
+ blizzard = 190356,
+ coldSnap = 235219,
+ coneOfCold = 120,
+ --fireBlast = 108853, - Fire Mage version, Frost should not have this and already listed fireBlast below
+ flurry = 44614,
+ freeze = 231596,
+ frostbolt = 116,
+ frostBomb = 112948,
+ frozenOrb = 84714,
+ frozenTouch = 205030,
+ fireBlast = 319836,
+ iceBarrier = 11426,
+ iceFloes = 108839,
+ iceForm = 198144,
+ iceLance = 30455,
+ iceNova = 157997,
+ icyVeins = 12472,
+ petFreeze = 33395,
+ rayOfFrost = 205021,
+ runeofPower = 116011,
+ removeCurse = 475,
+ summonWaterElemental = 31687,
+ waterbolt = 31707,
+ },
+ artifacts = {
+ -- icyHand = 220817,
+ },
+ buffs = {
+ brainFreeze = 190446,
+ concentratedCoolness = 198148,
+ freezingRain = 270232,
+ frostBomb = 112948,
+ fingersOfFrost = 44544,
+ icyVeins = 12472,
+ chainReaction = 195418,
+ zannesuJourney = 226852,
+ timeWarp = 80353,
+ iceFloes = 108839,
+ iceForm = 198144,
+ iceBarrier = 11426,
+ icicles = 205473,
+ frozenMass = 242253,
+ },
+ debuffs = {
+ chainsOfIce = 65173,
+ chilled = 205708,
+ frostBomb = 112948,
+ frostNova = 122,
+ iceNova = 157997,
+ wintersChill = 228358,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ --articGale = 205038,
+ boneChilling = 205027,
+ cometStorm = 153595,
+ chainReaction = 278309,
+ ebonbolt = 257537,
+ --frostBomb = 112948,
+ frozenTouch = 205030,
+ frigidWinds = 235224,
+ freezingRain = 270233,
+ focusMagic = 321358,
+ glacialSpike = 199786,
+ glacialInsulation = 235297,
+ iceNova = 157997,
+ iceFloes = 108839,
+ lonelyWinter = 205024,
+ rayOfFrost = 205021,
+ splittingIce = 56377,
+ thermalVoid = 155149,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ alterTime = 108978,
+ arcaneBrilliance = 1459,
+ arcaneExplosion = 1449,
+ blink = 1953,
+ blizzard = 10,
+ coneOfCold = 120,
+ conjureManaGem = 759,
+ conjureRefreshment = 42955,
+ conjureRefreshmentTable = 43987,
+ counterspell = 2139,
+ deepFreeze = 44572,
+ evocation = 12051,
+ fireBlast = 2136,
+ flamestrike = 2120,
+ frostArmor = 7302,
+ frostfireBolt = 44614,
+ frostNova = 122,
+ iceBlock = 45438,
+ iceLance = 30455,
+ invisibility = 66,
+ manaBomb = 125430,
+ mageArmor = 6117,
+ mirrorImage = 55342,
+ moltenArmor = 30482,
+ polymorph = 118,
+ removeCurse = 475,
+ slowFall = 130,
+ spellsteal = 30449,
+ timeWarp = 80353,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ -- arcaneIntellect = 1459,
+ -- disciplinaryCommand = 327365,
+ -- expandedPotential = 327489,
+ -- focusMagic = 321358,
+ -- freezingWinds = 327364,
+ -- iceBlock = 45438,
+ -- incantersFlow = 1463,
+ -- slowFall = 130,
+ -- slickIce = 327509,
+ -- runeOfPower = 116014,
+ },
+ conduits = {
+
+ },
+ covenants = {
+ -- deathborne = 324220,
+ -- mirrorsOfTorment = 314793,
+ -- radiantSpark = 307443,
+ -- shiftingPower = 314791,
+ },
+ debuffs = {
+ -- frostNova = 122,
+ -- mirrorsOfTorment = 314793,
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+ -- disciplinaryCommand = 327365,
+ -- sunKingsBlessings = 333313,
+ -- grislyIcicle = 333393,
+ -- firestorm = 333097,
+
+ },
+ talents = {
+ -- incantersFlow = 1463,
+ -- iceWard = 205036,
+ -- focusMagic = 321358,
+ -- ringOfFrost = 113724,
+ -- runeOfPower = 116011,
+ -- shimmer = 212653,
+ --unstableMagic = 157976,
+ },
+ },
+ },
+ MONK = {
+ -- Brewmaster
+ [268] = {
+ abilities = {
+ -- blackoutKick = 205523,
+ -- breathOfFire = 115181,
+ -- celestialBrew = 322507,
+ -- clash = 324312,
+ -- invokeNiuzao = 132578,
+ -- kegSmash = 121253,
+ -- purifyingBrew = 119582,
+ -- spinningCraneKick = 322729,
+ -- zenMeditation = 115176
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ -- eyeOfTheTiger = 196608,
+ -- ironskinBrew = 215479,
+ -- blackoutCombo = 228563,
+ -- purifiedChi = 325092,
+ -- rushingJadeWind = 116847,
+ -- zenMeditation = 115176,
+ -- celestialBrew = 322507,
+ -- invokeNiuzao = 132578,
+ -- counterStrike = 383800,
+ -- hitScheme = 383696,
+ -- shuffle = 322120,
+ -- invokeNiuzaoTheBlackOx = 132578,
+ -- weaponsOfOrder = 387184,
+ -- charredPassions = 386965,
+ -- bonedustBrew = 386276,
+ },
+ debuffs = {
+ -- breathOfFire = 146222,
+ -- moderateStagger = 124274,
+ -- heavyStagger = 124273,
+ -- kegSmash = 121253,
+ -- weaponsOfOrder = 387184,
+ -- bonedustBrew = 386276,
+ },
+ glyphs = {
+ },
+ talents = {
+ },
+ },
+ -- Mistweaver
+ [270] = {
+ abilities = {
+ -- envelopingMist = 124682,
+ -- essenceFont = 191837,
+ -- invokeYulon = 322118,
+ -- reawaken = 212051,
+ -- renewingMist = 115151,
+ -- revival = 115310,
+ -- risingSunKick = 107428,
+ -- soothingMist = 115175,
+ -- thunderFocusTea = 116680,
+ -- invokeYulonTheJadeSerpent = 322118,
+ -- invokeChiJiTheRedCrane = 325197,
+ -- fortifyingBrew = 243435,
+ -- zenFocusTea = 209584,
+ -- transcendenceTransfer = 119996,
+ -- sheilunsGift = 399491,
+ -- detox = 115450,
+ -- manaTea = 115294,
+ },
+ artifacts = {
+ },
+ buffs = {
+ -- weaponsOfOrder = 310454,
+ -- fortifyingBrew = 243435,
+ -- soothingMist = 115175,
+ -- renewingMist = 115151,
+ -- envelopingMist = 124682,
+ -- thunderFocusTea = 116680,
+ -- lifeCyclesEnvelopingMist = 197919,
+ -- lifeCyclesVivify = 197916,
+ -- surgeOfMist = 246328,
+ -- danceOfMist = 247891,
+ -- upliftTrance = 197206,
+ -- refreshingJadeWind = 196725,
+ -- lifeCocoon = 116849,
+ -- transcendence = 101643,
+ -- tigersLust = 116841,
+ -- teachingsOfTheMonastery = 202090,
+ -- diffuseMagic = 122783,
+ -- dempenHarm = 122278,
+ -- innervate = 29166,
+ -- symbolOfHope = 64901,
+ -- wayOfTheCrane = 216113,
+ -- essenceFont = 191840,
+ -- risingMist = 22170,
+ -- soothingMistJadeStatue = 198533,
+ -- envelopingBreath = 325209,
+ -- invokeChiJiTheRedCrane = 343820,
+ -- ancientTeachingOfTheMonastery = 347553,
+ -- fallenOrder = 326860,
+ -- vivaciousVivification = 392883,
+ -- rushingJadeWind = 116847,
+ -- ancientTeachings = 388023,
+ -- sheilunsGift = 399491,
+ -- manaTea = 115867,
+ },
+ debuffs = {
+ -- mysticTouch = 113746,
+ },
+ glyphs = {
+ },
+ talents = {
+ },
+ runeforges = {
+ -- ancientTeachingsOfTheMonastery = 337172,
+ -- tearOfMorning = 337473,
+ }
+ },
+ -- Windwalker
+ [269] = {
+ abilities = {
+ energizingBrew = 115288,
+ fistsOfFury = 113656,
+ flyingSerpentKick = 101545,
+ flyingSerpentKickEnd = 115057,
+ legacyOfTheWhiteTiger = 116781,
+ risingSunKick = 107428,
+ spinningFireBlossom = 115073,
+ stormEarthAndFire = 137639,
+ stormEarthAndFireFixate = 138130,
+ tigereyeBrew = 116740,--1247275,
+ touchOfKarma = 122470,
+ },
+ artifacts = {
+ },
+ buffs = {
+ comboBreakerBoK = 116768,
+ comboBreakerTP = 118864,
+ energizingBrew = 115288,
+ sparring = 116023,
+ stormEarthAndFire = 137639,
+ tigereyeBrew = 1247279,
+ tigereyeBrewUse = 1247275,
+ -- alphaTiger = 287504,
+ -- fortifyingBrew = 243435,
+ -- invokersDelight = 338321,
+ -- blackoutKick = 116768,
+ -- chiEnergy = 337571,
+ -- danceOfChiJi = 325202,
+ -- hitCombo = 196741,
+ -- pressurePoint = 247255,
+ -- rushingJadeWind = 116847,
+ -- serenity = 152173,
+ -- stormEarthAndFire = 137639,
+ -- swiftRoundhouse = 278710,
+ -- theEmperorsCapacitor = 235054,
+ -- touchOfKarma = 122470,
+ -- transferThePower = 195321,
+ -- whirlingDragonPunch = 152175,
+ -- fallenOrder = 326860,
+ -- bonedustBrew = 325216,
+ -- teachingsOfTheMonastery = 202090,
+ },
+ debuffs = {
+ risingSunKick = 130320,
+ stormEarthAndFireFixate = 138130,
+ -- disable = 116095,
+ -- disableRoot = 116706,
+ -- markOfTheCrane = 228287,
+ -- mortalWounds = 115804,
+ -- risingFist = 242259,
+ -- skyreachExhaustion = 337341,
+ -- jadefireBrand = 395414,
+ },
+ glyphs = {
+ -- glyphOfRisingTigerKick = 125151,
+ },
+ talents = {
+ },
+ traits = {
+ -- gloryOfTheDawn = 288634,
+ -- openPalmStrikes = 279918,
+ -- swiftRoundhouse = 277669,
+ },
+ runeforges = {
+ -- fatalTouch = 337296
+ }
+ },
+ -- All
+ Shared = {
+ abilities = {
+ blackoutKick = 100784,
+ cracklingJadeLightning = 117952,
+ detox = 115450,
+ disable = 116095,
+ expelHarm = 115072,
+ fortifyingBrew = 115203,
+ grappleWeapon = 117368,
+ healingSphere = 115460,
+ jab = 100780,
+ legacyOfTheEmperor = 115921,
+ -- legSweep = 119381,
+ nimbleBrew = 137562,
+ paralysis = 115078,
+ provoke = 115546,
+ reawaken = 1245934,
+ resuscitate = 115178,
+ roll = 109132,
+ spearHandStrike = 116705,
+ spinningCraneKick = 101546,
+ stanceOfTheFierceTiger = 103985,
+ tigerPalm = 100787,
+ touchOfDeath = 115080,
+ transcendence = 101643,
+ transcendenceTransfer = 119996,
+ -- touchOfFatality = 169340,
+ -- vivify = 116670,
+ -- zenFlight = 125883,
+ zenMeditation = 115176,
+ zenPilgrimage = 126892,
+ zenPilgrimageReturn = 126895,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ dampenHarm = 122278,
+ deathNote = 121125,
+ diffuseMagic = 122783,
+ fortifyingBrew = 126456,
+ legacyOfTheEmperor = 117666,
+ legacyOfTheWhiteTiger = 116781,
+ spinningCraneKick = 101546,
+ tigerPower = 125359,
+ -- theEmperorsCapacitor = 235054,
+ -- transcendence = 101643,
+ -- weaponsOfOrder = 328908, --310454,
+ -- weaponsOfOrderWW = 310454, --311054,
+ -- prideful = 340880,
+ -- vivaciousVivification = 392883,
+ -- powerStrikes = 129914, --bonus chi proc buff
+ -- hitCombo = 196741,
+ -- kicksOfFlowingMomentum = 394944,
+ -- blackoutReinforcement = 424454,
+ arcaneBrilliance = 1459, -- Mage Shared Buff
+ blessingOfKings = 20217, -- Paladin Shared Buff
+ dalaranBrilliance = 61316, -- Mage Shared Buff
+ markOfTheWild = 117666, -- Druid Shared Buff
+ leaderOfThePack = 24932, -- Druid Shared Buff
+ },
+ conduits = {
+ -- calculatedStrikes = 336526,
+ -- coordinatedOffensive = 336598,
+ -- pustuleEruption = 351094
+ },
+ covenants = {
+ -- bonedustBrew = 325216,
+ -- faelineStomp = 327104,
+ -- fallenOrder = 326860,
+ -- weaponsOfOrder = 310454,
+ },
+ debuffs = {
+ grappleWeapon = 117368,
+ zenSphere = 124098,
+ -- bonedustBrew = 325216,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ ascencion = 115396,
+ celerity = 115173,
+ chargingOxWave = 119392,
+ chiBrew = 115399,
+ chiBurst = 123986,
+ chiTorpedo = 115008,
+ chiWave = 115098,
+ dampenHarm = 122278,
+ diffuseMagic = 122783,
+ healingElixirs = 122280,
+ invokeXuenTheWhiteTiger = 123904,
+ legSweep = 119381,
+ momentum = 115174,
+ powerStrikes = 121817,
+ ringOfPeace = 116844,
+ rushingJadeWind = 116847,
+ tigersLust = 116841,
+ zenSphere = 124081,
+ },
+ },
+ },
+ PALADIN = {
+ -- Holy
+ [65] = {
+ abilities = {
+ absolution = 212056,
+ auraMastery = 31821,
+ barrierOfFaith = 148039,
+ beaconOfFaith = 156910,
+ beaconOfLight = 53563,
+ beaconOfVirtue = 200025,
+ blessingOfAutumn = 388010,
+ blessingOfSpring = 388013,
+ blessingOfSummer = 388007,
+ blessingOfWinter = 388011,
+ cleanse = 4987,
+ divineProtection = 498,
+ holyLight = 82326,
+ holyShock = 20473,
+ lightOfDawn = 85222,
+ lightOfTheMartyr = 183998,
+ --lightsHammer = 114158,
+
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ --sealOfClarity = 384810,
+ avengingCrusader = 216331,
+ auraMastery = 31821,
+ avengingWrath = 31884,
+ avengingWrathCrit = 294027,
+ afterimage = 400745,
+ beaconOfLight = 53563,
+ beaconOfFaith = 156910,
+ blessingOfSacrifice = 6940,
+ beaconOfVirtue = 200025,
+ --bestowFaith = 223306,
+ --glimmerOfLight = 287280,
+ blessingOfDawn = 337747,
+ blessingOfDusk = 385126,
+ divineProtection = 498,
+ divinePurpose = 223819,
+ --ferventMartyr = 223316,
+ infusionOfLight = 54149,
+ innervate = 29166,
+ --ruleOfLaw = 214202,
+ liberation = 461471,
+ --theLightSaves = 200423,
+ vindicator = 200376,
+ symbolOfHope = 64901,
+ divineSteed = 254474,
+ --maraadsBreath = 340459,
+ unendingLight = 394709,
+ untemperedDedication = 339990,
+ veneration = 392939,
+ },
+ debuffs = {
+ judgement = 214222,
+ judgmentOfLight = 196941,
+ --glimmerOfLight = 287280,
+ consecration = 204242,
+ dawnWillCome = 364851,
+
+ },
+ glyphs = {
+
+ },
+ talents = {
+ avengingCrusader = 216331,
+ --awakening = 248033,
+ beaconOfFaith = 156910,
+ beaconOfVirtue = 200025,
+ --bestowFaith = 223306,
+ blessingOfSummer = 388007,
+ breakingDawn = 387879,
+ crusadersMight = 196926,
+ --glimmerOfLight = 325966,
+ holyPrism = 114165,
+ judgmentOfLight = 183778,
+ --lightsHammer = 114158,
+ --ruleOfLaw = 214202,
+ --sanctifiedWrath = 53376,
+ savedByTheLight = 157047,
+ divineToll = 375576,
+ },
+ traits = {
+ graceoftheJusticar = 278593,
+ indomitableJustice = 275496,
+ },
+ runeforges = {
+ shadowbreaker = 337812,
+ shockBarrier = 337825,
+ maraadsDyingBreath = 340458,
+ }
+ },
+ -- Protection
+ [66] = {
+ abilities = {
+ ardentDefender = 31850,
+ avengersShield = 31935,
+ blessedHammer = 229976,
+ cleanseToxins = 213644,
+ divineProtection = 498,
+ guardianOfAncientKings = 86659,
+ hammerOfTheRighteous = 53595,
+ judgment = 275779,
+ judgmentsOfTheWise = 105424,
+ rebuke = 96231,
+ righteousFury = 25780,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ ardentDefender = 31850,
+ avengingWrath = 31884,
+ blessingOfSpellwarding = 204018,
+ bulwarkOfOrder = 209388,
+ consecration = 188370,
+ divineShield = 642,
+ divinePurpose = 223819,
+ guardianOfAncientKings = 86659,
+ shieldOfTheRighteous = 132403,
+ shiningLight = 327510,
+ avengersValor = 197561,
+ divineSteed = 254474,
+ royalDecree = 340147,
+ },
+ debuffs = {
+ blessedHammer = 204301,
+ judgmentOfLight = 196941,
+ judgment = 197277,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ layOnHands = 633,
+ blessedHammer = 204019,
+ hammerOfTheRighteous = 53595,
+ innerLight = 386568,
+ redoubt = 280373,
+ holyShield = 152261,
+ grandCrusader = 85043,
+ -- shiningLight = 321136, -- No longer a talent?
+ consecratedGround = 204054,
+ improvedLayOnHands = 393027,
+ inspiringVanguard = 393022,
+ ardentDefender = 31850,
+ barricadeOfFaith = 385726,
+ consecrationInFlame = 379022,
+ improvedHolyShield = 393030,
+ sanctuary = 379021,
+ bulwarkOfOrder = 209389,
+ improvedArdentDefender = 393114,
+ blessingOfSpellwarding = 204018,
+ lightOfTheTitans = 378405,
+ strengthInAdversity = 393071,
+ crusadersResolve = 380188,
+ tyrsEnforcer = 378285,
+ relentlessInquisitor = 383388,
+ aveningWrathMight = 31884,
+ sentinel = 389539,
+ handOTheProtector = 315924,
+ strengthOfConviction = 379008,
+ resoluteDefender = 385422,
+ bastionOfLight = 378974,
+ guardianOfAncientKings = 86659,
+ crusadersJudgement = 204023,
+ uthersCounsel = 378425,
+ focusedEnmity = 378845,
+ soaringShield = 378457,
+ giftOfTheGoldenValkyr = 378279,
+ eyeOfTyr = 387174,
+ righteousProtector = 204074,
+ faithInTheLight = 379043,
+ ferrenMarcussFervor = 378762,
+ faithsArmor = 379017,
+ finalStand = 204077,
+ divineToll = 375576,
+ momentOfGlory = 327193,
+ bulwarkOfTheRighteousFury = 386653,
+ divineResonance = 386738,
+ quickenedInvocations = 379391,
+ },
+ traits = {
+ bulwarkOfLight = 272976,
+ },
+ },
+ -- Retribution
+ [70] = {
+ abilities = {
+ -- divineProtection = 403876,
+ divineStorm = 53385,
+ emancipation = 121783,
+ exorcism = 879,
+ guardianOfAncientKings = 86659,
+ hammerOfTheRighteous = 53595,
+ inquisition = 84963,
+ judgmentsOfTheBold = 111529,
+ sealOfJustice = 20164,
+ templarsVerdict = 85256,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ ancientPower = 86700,
+ -- crusade = 231895,
+ -- divineArbiter = 406975,
+ divineCrusader = 144595,
+ -- divinePurpose = 223819,
+ -- divineRight = 278523,
+ -- empyreanLegacy = 387178,
+ -- empyreanPower = 326733, --286393,
+ inquisition = 84963,
+ -- finalVerdict = 414949,
+ -- selflessHealer = 114250,
+ -- templarStrikes = 406648,
+ },
+ debuffs = {
+ -- executionSentence = 267798,
+ -- expurgation = 383346,
+ -- finalReckoning = 343721,
+ },
+ glyphs = {
+ -- glyphOfWingedVengeance = 57979,
+ },
+ talents = {
+ },
+ traits = {
+ -- divineRight = 278523,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ absolution = 450761,
+ avengingWrath = 31884,
+ blessingOfKings = 20217,
+ blessingOfMight = 19740,
+ blindingLight = 115750,
+ cleanse = 4987,
+ crusaderStrike = 35395,
+ devotionAura = 31821,
+ divineProtection = 498,
+ divineShield = 642,
+ flashOfLight = 19750,
+ hammerOfJustice = 853,
+ hammerOfWrath = 24275,
+ handOfFreedom = 1044,
+ handOfProtection = 1022,
+ handOfSacrifice = 6940,
+ handOfSalvation = 1038,
+ judgment = 20271,
+ layOnHands = 633,
+ rebuke = 96231,
+ reckoning = 62124,
+ redemption = 7328,
+ righteousFury = 25780,
+ sealOfCommand = 105361,
+ sealOfInsight = 20165,
+ sealOfRighteousness = 20154,
+ sealOfTruth = 31801,
+ turnEvil = 10326,
+ wordOfGlory = 85673,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ avengingWrath = 31884,
+ -- blessingOfProtection = 1022,
+ -- consecration = 26573,
+ -- concentrationAura = 317920,
+ -- crusaderAura = 32223,
+ -- devotionAura = 465,
+ divinePurpose = 90174,
+ -- divineResonance = 384029,
+ -- divineShield = 642,
+ -- divineSteed = 221883,
+ -- echoesOfWrath = 423590,
+ holyAvenger = 105809,
+ -- retributionAura = 183435,
+ -- seraphim = 152262,
+ sealOfCommand = 105361,
+ -- vanquishersHammer = 328204,
+ },
+ conduits = {
+ -- expurgation = 339371,
+ -- templarsVindication = 339531,
+ },
+ covenants = {
+ --ashenHallow = 316958,
+ --blessingOfTheSeasons = 328278,
+ --blessingOfAutumn = 388010,
+ --blessingOfSpring = 388013,
+ --blessingOfSummer = 388007,
+ --blessingOfWinter = 388011,
+ --vanquishersHammer = 328204,
+ },
+ debuffs = {
+ -- blindingLight = 105421,
+ forbearance = 25771,
+ -- hammerOfJustice = 853,
+ -- judgment = 197277,
+ -- turnEvil = 10326,
+ -- handOfReckoning = 62124,
+ },
+ glyphs = {
+ -- glyphOfFireFromHeavens = 57954,
+ -- glyphOfPillarOfLight = 146959,
+ -- glyphOfTheLuminousCharger = 89401,
+ -- glyphOfTheQueen = 212642,
+ },
+ talents = {
+ clemency = 105622,
+ divinePurpose = 86172,
+ eternalFlame = 114163,
+ evilIsAPointOfView = 110301,
+ executionSentence = 114157,
+ fistOfJustice = 105593,
+ handOfPurity = 114039,
+ holyAvenger = 105809,
+ holyPrism = 114165,
+ lightsHammer = 114158,
+ longArmOfTheLaw = 87172,
+ pursuitOfJustice = 26023,
+ repentance = 20066,
+ sacredShield = 20925,
+ sanctifiedWrath = 53376,
+ selflessHealer = 85804,
+ speedOfLight = 85499,
+ unbreakableSpirit = 114154,
+ },
+ runeforges = {
+ -- finalVerdict = 337247,
+ -- theMadParagon = 337594,
+ -- theMagistratesJudgment = 337681,
+ -- vanguardsMomentum = 337638,
+ }
+ },
+ },
+ PRIEST = {
+ -- Discipline
+ [256] = {
+ abilities = {
+ angelicFeather = 121536,
+ --clarityOfWill = 152118,
+ divineStar = 110744,
+ desperatePrayer = 19236,
+ evangelism = 246287,
+ flashHeal = 2061,
+ halo = 120517,
+ holyNova = 132157,
+ leapOfFaith = 73325,
+ mindbender = 123040,
+ mindBlast = 8092,
+ mindControl = 205364,
+ mindSear = 48045,
+ mindSoothe = 453,
+ mindVision = 2096,
+ painSuppression = 33206,
+ penance = 47540,
+ --plea = 200829,
+ powerInfusion = 10060,
+ powerWordBarrier = 62618,
+ powerWordRadiance = 194509,
+ powerWordShield = 17,
+ powerWordSolace = 129250,
+ psychicScream = 8122,
+ purgeTheWicked = 204197,
+ purify = 527,
+ rapture = 47536,
+ schism = 214621,
+ shadowfiend = 34433,
+ shadowMend = 186263,
+ shiningForce = 204263,
+ sinsOfTheMany = 198076,
+ spiritShell = 109964,
+ smite = 585,
+ },
+ artifacts = {
+ --sinsOfTheMany = 198074,
+ },
+ buffs = {
+ angelicFeather = 121557,
+ atonement = 194384,
+ bodyAndSoul = 65081,
+ borrowedTime = 197763,
+ depthOfTheShadows = 275544,
+ --clarityOfWill = 152118,
+ innervate = 29166,
+ overloadedWithLight = 223166,
+ penitent = 246519,
+ --powerInfusion = 10060,
+ powerOfTheDarkSide = 198069,
+ powerWordShield = 17,
+ rapture = 47536,
+ speedOfThePious = 197767,
+ spiritShell = 109964,
+ symbolOfHope = 64901,
+ },
+ debuffs = {
+ weakenedSoul = 6788,
+ purgeTheWicked = 204213,
+ schism = 214621,
+ smite = 585,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ angelicFeather = 121536,
+ bodyAndSoul = 64129,
+ castigation = 193134,
+ --clarityOfWill = 152118,
+ contrition = 197419,
+ divineStar = 110744,
+ dominantMind = 205367,
+ evangelism = 246287,
+ --grace = 200309,
+ halo = 120517,
+ lenience = 238063,
+ masochism = 193063,
+ mindbender = 123040,
+ --powerInfusion = 10060,
+ powerWordSolace = 129250,
+ purgeTheWicked = 204197,
+ psychicVoice = 196704,
+ schism = 214621,
+ shadowCovenant = 314867,
+ shieldDiscipline = 197045,
+ spiritShell = 109964,
+ sinsOfTheMany = 280391,
+ shiningForce = 204263,
+ --thePenitent = 200347,
+ twistOfFate = 265259,
+ },
+ traits = {
+ giftOfForgiveness = 277680,
+ },
+ },
+ -- Holy
+ [257] = {
+ abilities = {
+ angelicFeather = 121536,
+ bodyAndMind = 214121,
+ circleOfHealing = 204883,
+ desperatePrayer = 19236,
+ divineHymn = 64843,
+ divineStar = 110744,
+ flashHeal = 2061,
+ guardianSpirit = 47788,
+ holyFire = 14914,
+ holyNova = 132157,
+ holyWordChastise = 88625,
+ holyWordSanctify = 34861,
+ holyWordSerenity = 2050,
+ holyWordSalvation = 265202,
+ heal = 2060,
+ leapOfFaith = 73325,
+ lightOfTuure = 33076,
+ prayerOfHealing = 596,
+ prayerOfMending = 33076,
+ purify = 527,
+ renew = 139,
+ smite = 585,
+ symbolOfHope = 64901,
+ },
+ artifacts = {
+ -- lightOfTuure = 208065,
+ },
+ buffs = {
+ angelicFeather = 121557,
+ blessingOfTuure = 196578,
+ divinity = 197031,
+ echoOfLight = 77489,
+ flashConcentration = 336267,
+ powerOfTheNaaru = 196489,
+ prayerOfMending = 41635,
+ renew = 139,
+ surgeOfLight = 114255,
+ spiritOfRedemption = 27827,
+ },
+ debuffs = {
+
+
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+ flashConcentration = 336266,
+ },
+ talents = {
+ angelicFeather = 121536,
+ apotheosis = 200183,
+ bodyAndSoul = 64129,
+ --bodyAndMind = 214121,
+ divineStar = 110744,
+ --divinity = 197031,
+ halo = 120517,
+ --piety = 197034,
+ shiningForce = 204263,
+ surgeOfLight = 109186,
+ --symbolOfHope = 64901,
+ },
+ },
+ -- Shadow
+ [258] = {
+ abilities = {
+ damnation = 341374,
+ darkAscension = 280711,
+ dispersion = 47585,
+ devouringPlague = 335467,
+ mindBlast = 8092,
+ mindBomb = 205369,
+ mindbender = 200174,
+ mindFlay = 15407,
+ mindSear = 48045,
+ mindVision = 2096,
+ powerInfusion = 10060,
+ powerWordShield = 17,
+ psychicHorror = 64044,
+ psychicScream = 8122,
+ purifyDisease = 213634,
+ shadowMend = 186263,
+ shadowWordVoid = 205351,
+ shadowfiend = 34433,
+ shadowform = 232698,
+ silence = 15487,
+ surrenderToMadness = 193223,
+ vampiricEmbrace = 15286,
+ vampiricTouch = 34914,
+ voidBolt = 205448,
+ devoidBolt = 343355,
+ voidEruption = 228260,
+ voidForm = 228264,
+ },
+ artifacts = {
+ lashOfInsanity = 238137,
+ massHysteria = 194378,
+ sphereOfInsanity = 194179,
+ toThePain = 193644,
+ touchOfDarkness = 194007,
+ unleashTheShadows = 194093,
+ voidCorruption = 194016,
+ voidTorrent = 205065,
+ },
+ buffs = {
+ chorusOfInsanity = 279572,
+ darkThoughts = 341207,
+ dispersion = 47585,
+ dissonantEchoes = 343144,
+ harvestedThoughts = 288343,
+ lingeringInsanity = 199849,
+ powerWordShield = 17,
+ powerInfusion = 10060,
+ shadowyInsight = 124430,
+ shadowform = 232698,
+ surrenderedSoul = 212570,
+ surrenderToMadness = 193223,
+ thoughtsHarvester = 288340,
+ unfurlingDarkness = 341282,
+ void = 211657,
+ voidForm = 194249,
+ voidTorrent = 205065,
+ zeksExterminatus = 236546, -- Legendary Cloak proc
+ },
+ debuffs = {
+ devouringPlague = 335467,
+ hungeringVoid = 345218,
+ mindFlay = 15407,
+ vampiricTouch = 34914,
+ weakenedSoul = 6788,
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+ shadowflamePrism = 336143,
+ },
+ talents = {
+ fortressOfTheMind = 193195,
+ deathAndMadness = 321291,
+ unfurlingDarkness = 341273,
+ bodyAndSoul = 64129,
+ sanlayn = 199855,
+ twistOfFate = 109142,
+ misery = 238558,
+ searingNightmare = 341385,
+ mindBomb = 205369,
+ psychicHorror = 64044,
+ auspiciousSpirits = 155271,
+ psychicLink = 199484,
+ shadowCrash = 205385,
+ damnation = 341374,
+ mindbender = 200174,
+ voidTorrent = 263165,
+ ancientMadness = 341240,
+ hungeringVoid = 345218,
+ surrenderToMadness = 319952,
+ },
+ traits = {
+ chorusOfInsanity = 278661,
+ deathThroes = 278659,
+ searingDialogue = 272788,
+ spitefulApparitions = 277682,
+ thoughtHarvester = 288340,
+ whispersOfTheDamned = 275722,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ ascendedBlast = 325283,
+ ascendedNova = 325020,
+ boonOfTheAscended = 325013,
+ desperatePrayer = 19236,
+ dispelMagic = 528,
+ fade = 586,
+ faeGuardians = 327661,
+ flashHeal = 2061,
+ leapOfFaith = 73325,
+ levitate = 1706,
+ massDispel = 32375,
+ massResurrection = 212036,
+ mindBlast = 8092,
+ mindControl = 605,
+ mindgames = 323673,
+ mindSoothe = 453,
+ mindVision = 2096,
+ powerInfusion = 10060,
+ powerWordFortitude = 21562,
+ powerWordShield = 17,
+ psychicScream = 8122,
+ resurrection = 2006,
+ shadowWordDeath = 32379,
+ shackleUndead = 9484,
+ shadowWordPain = 589,
+ smite = 585,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ classHallSpeed = 224098,
+ boonOfTheAscended = 325013,
+ faeGuardians = 327661,
+ levitate = 111759,
+ powerWordFortitude = 21562,
+ powerWordShield = 17,
+
+ },
+ conduits = {
+ dissonantEchoes = 338342,
+ },
+ covenants = {
+ boonOfTheAscended = 325013,
+ ascendedBlast = 325315,
+ ascendedEruption = 325326,
+ ascendedNova = 325020,
+ faeGuardians = 327661,
+ mindgames = 323673,
+ unholyNova = 324724,
+ },
+ debuffs = {
+ shadowWordPain = 589,
+ weakenedSoul = 6788,
+ wrathfulFaerie = 327703,
+
+ },
+ glyphs = {
+
+ },
+ talents = {
+
+ },
+ },
+ },
+ ROGUE = {
+ -- Assassination
+ [259] = {
+ abilities = {
+ -- ambush = 8676,
+ -- crimsonTempest = 121411,
+ -- deadlyPoison = 315584,
+ -- envenom = 32645,
+ -- eviscerate = 196819,
+ -- exsanguinate = 200806,
+ -- fanOfKnives = 51723,
+ -- garrote = 703,
+ -- mutilate = 1329,
+ -- poisonedKnife = 185565,
+ -- rupture = 1943,
+ -- shadowstep = 36554,
+ -- sinisterStrike = 1752,
+ -- vendetta = 79140,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ blindside = 121153,
+ deadlyPoison = 2823,
+ elaboratePlanning = 193641,
+ envenom = 32645,
+ leechingPoison = 108211,
+ sharpenedBlades = 272916,
+ masterAssassin = 256735,
+ subterfuge = 115192,
+ theDreadlordsDeceit = 208692,
+ },
+ debuffs = {
+ crimsonTempest = 121411,
+ deadlyPoison = 2818,
+ garrote = 703,
+ internalBleeding = 154953,
+ rupture = 1943,
+ surgeOfToxins = 192425,
+ toxicBlade = 245389,
+ vendetta = 79140,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ blindside = 328085,
+ crimsonTempest = 121411,
+ elaboratePlanning = 193640,
+ elusiveness = 79008,
+ exsanguinate = 200806,
+ hiddenBlades = 270061,
+ internalBleeding = 154904,
+ ironWire = 196861,
+ leechingPoison = 280716,
+ masterAssassin = 255989,
+ masterPoisoner = 196864,
+ nightstalker = 14062,
+ poisonBomb = 255544,
+ subterfuge = 108208,
+ venomRush = 152152,
+ },
+ traits = {
+ doubleDose = 273007,
+ echoingBlades = 287649,
+ sharpenedBlades = 272911,
+ shroudedSuffocation = 278666,
+ twistTheKnife = 273488,
+ },
+ runeforges = {
+ dashingScoundrel = 340081,
+ doomblade = 340082,
+ zoldyckInsignia = 340083,
+ duskwalkersPatch = 340084,
+ },
+ },
+ -- Outlaw
+ [260] = {
+ abilities = {
+ adrenalineRush = 13750,
+ ambush = 8676,
+ betweenTheEyes = 315341,
+ bladeFlurry = 13877,
+ bladeRush = 271877,
+ blind = 2094,
+ curseOfTheDreadblades = 202665,
+ dispatch = 2098,
+ ghostlyStrike = 196937,
+ gouge = 1776,
+ grapplingHook = 195457,
+ killingSpree = 51690,
+ masteryMainGauche = 76806,
+ pistolShot = 185763,
+ riposte = 199754,
+ rollTheBones = 315508,
+ sinisterStrike = 193315,
+ dreadblades = 343142,
+ },
+ artifacts = {
+ blackPowder = 216230,
+ bladeDancer = 202507,
+ bladeMaster = 202628,
+ blunderbuss = 202897,
+ blurredTime = 202769,
+ cannonballBarrage = 185767,
+ curseOfTheDreadblades = 202665,
+ cursedEdge = 202463,
+ cursedSteel = 214929,
+ deception = 202755,
+ fatesThirst = 202514,
+ fatebringer = 202524,
+ fortuneStrikes = 202530,
+ fortunesBoon = 202907,
+ fortunesStrike = 202521,
+ ghostlyShell = 202533,
+ greed = 202820,
+ gunslinger = 202522,
+ hiddenBlade = 202573,
+ killingSpree = 51690,
+ loadedDice = 238139,
+ },
+ buffs = {
+ adrenalineRush = 13750,
+ alacrity = 193538,
+ bladeFlurry = 13877,
+ blunderbuss = 202895,
+ broadside = 193356,
+ buriedTreasure = 199600,
+ deadShot = 272940,
+ grandMelee = 193358,
+ greenskinsWaterloggedWristcuffs = 209420,
+ hiddenBlade = 202754,
+ jollyRoger = 199603,
+ loadedDice = 256171,
+ opportunity = 195627,
+ ruthlessPrecision = 193357,
+ rollTheBones = {
+ broadside = 193356,
+ buriedTreasure = 199600,
+ grandMelee = 193358,
+ ruthlessPrecision = 193357,
+ skullAndCrossbones = 199603,
+ trueBearing = 193359,
+ },
+ sharkInfestedWaters = 193357,
+ skullAndCrossbones = 199603,
+ snakeeeyes = 275863, --typo, leaving in not to break stuff
+ snakeeyes = 275863,
+ swordplay = 211669,
+ trueBearing = 193359,
+ wits = 288988,
+ },
+ debuffs = {
+ ghostlyStrike = 196937,
+ gouge = 1776,
+ betweenTheEyes = 199804,
+ preyOnTheWeak = 255909,
+ blind = 2094,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ acrobaticStikes = 196924, -- typo, leaving in to not break old profiles
+ acrobaticStrikes = 196924,
+ dirtyTricks = 108216,
+ bladeRush = 271877,
+ ghostlyStrike = 196937,
+ grapplingHook = 256188,
+ hitAndRun = 196922,
+ ironStomach = 193546,
+ killingSpree = 51690,
+ weaponmaster = 200733,
+ quickDraw = 196938,
+ blindingPowder = 256165,
+ loadedDice = 256170,
+ dancingSteel = 272026,
+ dreadblades = 343142,
+ },
+ traits = {
+ deadshot = 272935,
+ aceupyoursleeve = 278676,
+ snakeeyes = 275846,
+ keepYourWitsAboutYou = 288979,
+
+ },
+ runeforges = {
+ greenskinsWickers = 340085,
+ guileCharm = 340086,
+ celerity = 340087,
+ concealedBlunderbuss = 340088,
+ },
+ conduits = {
+ countTheOdds = 341546,
+ },
+ },
+ -- Subtlety (MoP Classic - 5.4.8)
+ [261] = {
+ abilities = {
+ ambush = 8676,
+ backstab = 53,
+ crimsonTempest = 121411,
+ eviscerate = 2098,
+ fanOfKnives = 51723,
+ hemorrhage = 16511,
+ preparation = 14185,
+ premeditation = 14183,
+ rupture = 1943,
+ shadowBlades = 121471,
+ shadowDance = 121471, -- MoP Classic uses same ID as Shadow Blades
+ shadowstep = 36554,
+ shurikenToss = 114014,
+ sliceAndDice = 5171,
+ vanish = 1856,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ anticipation = 115189, -- Talent buff for extra combo points
+ masterOfSubtlety = 31665, -- Shadow Dance / Stealth buff
+ shadowBlades = 121471,
+ shadowDance = 121471, -- MoP Classic uses same ID as Shadow Blades
+ shadowmeld = 58984, -- Night Elf racial
+ sliceAndDice = 5171,
+ sleightOfHand = 145211, -- Proc from Ambush
+ stealth = 1784,
+ subterfuge = 115192, -- Talent - Stealth linger
+ vanish = 1856,
+ },
+ debuffs = {
+ crimsonTempest = 122233,
+ findWeakness = 91023, -- Shadow Dance debuff
+ hemorrhage = 16511,
+ rupture = 1943,
+ },
+ glyphs = {
+ glyphOfAmbush = 56813,
+ glyphOfBackstab = 63254,
+ glyphOfHemorrhage = 56807,
+ glyphOfPreparation = 63249,
+ glyphOfShadowDance = 63253,
+ glyphOfSliceAndDice = 146638,
+ glyphOfVanish = 56814,
+ },
+ talents = {
+ -- Tier 1 (Level 15)
+ nightstalker = 14062,
+ subterfuge = 108208,
+ shadowFocus = 108209,
+ -- Tier 2 (Level 30) - Shared
+ deadlyThrow = 26679,
+ combatReadiness = 74001,
+ -- Tier 3 (Level 45)
+ cheatDeath = 31230,
+ leechingPoison = 108211,
+ elusiveness = 79008,
+ -- Tier 4 (Level 60)
+ cloakAndDagger = 138106,
+ shadowstep = 36554,
+ burstOfSpeed = 108212,
+ -- Tier 5 (Level 75)
+ preyOnTheWeak = 131511,
+ paralyticPoison = 108215,
+ dirtyTricks = 108216,
+ -- Tier 6 (Level 90)
+ shurikenToss = 114014,
+ markedForDeath = 137619,
+ anticipation = 114015,
+ },
+ traits = {
+
+ },
+ runeforges = {
+
+ },
+ conduits = {
+
+ },
+ },
+ -- All (MoP Classic - 5.4.8)
+ Shared = {
+ abilities = {
+ ambush = 8676,
+ blind = 2094,
+ cheapShot = 1833,
+ cloakOfShadows = 31224,
+ combatReadiness = 74001, -- MoP defensive CD
+ cripplingPoison = 3408,
+ deadlyPoison = 2823,
+ distract = 1725,
+ evasion = 5277,
+ eviscerate = 2098,
+ feint = 1966,
+ gouge = 1776,
+ instantPoison = 6603, -- MoP version
+ kick = 1766,
+ kidneyShot = 408,
+ mindNumbingPoison = 5761, -- MoP version (previously Crippling in later expansions)
+ paralyticPoison = 108211, -- MoP poison
+ pickLock = 1804,
+ pickPocket = 921,
+ redirect = 73981, -- MoP ability
+ recuperate = 73651, -- MoP healing finisher
+ sap = 6770,
+ shroudOfConcealment = 114018, -- MoP group stealth
+ sinisterStrike = 1752,
+ sliceAndDice = 5171,
+ sprint = 2983,
+ stealth = 1784,
+ throw = 2764, -- MoP version
+ tricksOfTheTrade = 57934,
+ vanish = 1856,
+ woundPoison = 8679,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ cloakOfShadows = 31224,
+ combatReadiness = 74001,
+ cripplingPoison = 3408,
+ deadlyPoison = 2823,
+ evasion = 5277,
+ feint = 1966,
+ instantPoison = 6603,
+ mindNumbingPoison = 5761,
+ paralyticPoison = 108211,
+ recuperate = 73651,
+ shroudOfConcealment = 114018,
+ sliceAndDice = 5171,
+ sprint = 2983,
+ stealth = 1784,
+ tricksOfTheTrade = 57934,
+ vanish = 1856,
+ woundPoison = 8679,
+ },
+ conduits = {
+
+ },
+ covenants = {
+
+ },
+ debuffs = {
+ blind = 2094,
+ cheapShot = 1833,
+ cripplingPoison = 3408,
+ garrote = 703,
+ gouge = 1776,
+ kidneyShot = 408,
+ mindNumbingPoison = 5761,
+ paralyticPoison = 113952, -- Debuff version
+ rupture = 1943,
+ sap = 6770,
+ woundPoison = 8679,
+ },
+ glyphs = {
+ glyphOfAdrenalineRush = 56803,
+ glyphOfBlind = 91299,
+ glyphOfCloakOfShadows = 63269,
+ glyphOfExposedArmor = 56805,
+ glyphOfFanOfKnives = 63254,
+ glyphOfFeint = 63257,
+ glyphOfGarrote = 56812,
+ glyphOfGouge = 56809,
+ glyphOfKick = 63252,
+ glyphOfKillingSpree = 63249,
+ glyphOfSap = 56798,
+ glyphOfSprint = 63250,
+ glyphOfTricksOfTheTrade = 63256,
+ },
+ talents = {
+ -- Tier 2 (Level 30) - Shared across all Rogue specs
+ deadlyThrow = 26679,
+ combatReadiness = 74001,
+ },
+ runeforges = {
+
+ },
+ },
+ },
+ SHAMAN = {
+ -- Elemental
+ [262] = {
+ abilities = {
+ earthquake = 61882,
+ earthShock = 8042,
+ eyeOfTheStorm = 157375,
+ fireBlast = 57984,
+ fireElemental = 198067,
+ hardenSkin = 118337,
+ immolate = 118297,
+ lavaBeam = 114074,
+ meteor = 117588,
+ pulverize = 118345,
+ windGust = 157331,
+ },
+ artifacts = {
+ },
+ conduits = {
+ callOfFlame = 338303,
+ },
+ buffs = {
+ ascendance = 114050,
+ bloodlust = 2825,
+ echoingShock = 320125,
+ elementalFocus = 16164,
+ elementalMastery = 16166,
+ emberTotem = 210658,
+ hardenSkin = 118337,
+ heroism = 32182,
+ iceFury = 210714,
+ lavaSurge = 77762,
+ masterOfTheElements = 260734,
+ powerOfTheMaelstrom = 191877,
+ resonanceTotem = 202192,
+ stormKeeper = 191634,
+ stormTotem = 210652,
+ surgeOfPower = 285514,
+ tailwindTotem = 210659,
+ tectonicThunder = 286949,
+ windGust = 263806,
+ },
+ debuffs = {
+ flameShock = 188389,
+ immolate = 118297,
+ lightningRod = 197209,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ aftershock = 273221,
+ ancestralGuidance = 108281,
+ ascendance = 114050,
+ earthenRage = 170374,
+ echoingShock = 320125,
+ echoOfTheElements = 333919,
+ elementalBlast = 117014,
+ iceFury = 210714,
+ liquidMagmaTotem = 192222,
+ masterOfTheElements = 16166,
+ primalElementalist = 117013,
+ staticDischarge = 342243,
+ stormElemental = 192249,
+ stormKeeper = 191634,
+ surgeOfPower = 262303,
+ unlimitedPower = 260895,
+
+ },
+ traits = {
+ naturalHarmony = 278697,
+ tectonicThunder = 286949,
+ },
+ },
+ -- Enhancement
+ [263] = {
+ abilities = {
+ feralSpirit = 51533,
+ fireNova = 1535,
+ lavaLash = 60103,
+ searingTotem = 3599,
+ shamanisticRage = 30823,
+ spiritWalk = 58875,
+ stormblast = 115356,
+ stormstrike = 17364,
+ windfuryWeapon = 8232,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ ascendance = 114051,
+ -- ashenCatalyst = 390371,
+ -- awakeningStorms = 462131,
+ -- cracklingSurge = 224127,
+ -- crashLightning = 187878,
+ -- clCrashLightning = 333964,
+ -- convergingStorms = 198300,
+ -- doomWinds = 384352,
+ -- earthenWeapon = 392375,
+ -- feralSpirit = 333957,
+ -- hailstorm = 334196,
+ -- hotHand = 215785,
+ -- icyEdge = 224126,
+ -- legacyOfTheFrostWitch = 384451,
+ -- maelstromOfElements = 394677,
+ maelstromWeapon = 53817,
+ -- moltenWeapon = 224125,
+ -- primordialWave = 327164,
+ -- splinteredElements = 354648,
+ -- tempest = 454015,
+ -- stormkeeper = 320137,
+ -- stormbringer = 201845,
+ },
+ debuffs = {
+ -- doomWinds = 335904,
+ -- lashingFlames = 334168,
+ },
+ glyphs = {
+ glyphOfFrostShock = 55443,
+ },
+ talents = {
+ },
+ traits = {
+ -- lightningConduit = 275389,
+ -- naturalHarmony = 278697,
+ -- primalPrimer = 272992,
+ -- strengthOfTheEarth = 273461,
+ },
+ },
+ -- Restoration
+ [264] = {
+ abilities = {
+ ancestralVision = 212048,
+ waterShield = 52127,
+ healingRain = 73920,
+ healingTideTotem = 108280,
+ healingWave = 77472,
+ manaTideTotem = 16191,
+ purifySpirit = 77130,
+ riptide = 61295,
+ spiritLinkTotem = 98008,
+ recallCloudburstTotem = 201764,
+ },
+ artifacts = {
+ -- giftOfTheQueen = 207778,
+ },
+ buffs = {
+ ascendance = 114052,
+ cloudburstTotem = 157504,
+ waterShield = 52127,
+ healingRain = 73920,
+ heavyRainfall = 338344,
+ jonatsFocus = 210607,
+ lavaSurge = 77762,
+ riptide = 61295,
+ tidalWaves = 53390,
+ unleashLife = 73685,
+ undulation = 216251,
+ swirlingCurrents = 338340,
+ },
+ conduits = {
+ heavyRainfall = 338343,
+ },
+ debuffs = {
+ flameShock = 188389,
+ },
+ glyphs = {
+ },
+ talents = {
+ ancestralVigor = 207401,
+ ancestralProtectionTotem = 207399,
+ ascendance = 114052,
+ cloudburstTotem = 157153,
+ deluge = 200076,
+ downpour = 207778,
+ earthenWallTotem = 198838,
+ echoOfTheElements = 108283,
+ flashFlood = 280614,
+ highTide = 157154,
+ sugeofEarth = 320746,
+ torrent = 200072,
+ undulation = 200071,
+ unleashLife = 73685,
+ wellspring = 197995,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ ascendance = 114049,
+ ancestralSpirit = 2008,
+ ancestralVision = 450762,
+ astralRecall = 556,
+ bindElemental = 76780,
+ bloodlust = 2825,
+ capacitorTotem = 108269,
+ chainHeal = 1064,
+ chainLightning = 421,
+ cleanseSpirit = 51886,
+ earthbindTotem = 2484,
+ earthElementalTotem = 2062,
+ earthShock = 8042,
+ farSight = 6196,
+ fireElementalTotem = 2894,
+ flameShock = 8050,
+ flametongueWeapon = 8024,
+ frostbrandWeapon = 8033,
+ frostShock = 8056,
+ ghostWolf = 2645,
+ groundingTotem = 8177,
+ healingRain = 73920,
+ healingStreamTotem = 5394,
+ healingSurge = 8004,
+ healingTideTotem = 108280,
+ heroism = 32182,
+ hex = 51514,
+ lightningBolt = 403,
+ lightningShield = 324,
+ magmaTotem = 8190,
+ primalStrike = 73899,
+ purge = 370,
+ rockbiterWeapon = 8017,
+ searingTotem = 3599,
+ spiritwalkersGrace = 79206,
+ stormlashTotem = 120668,
+ totemicRecall = 36936,
+ tremorTotem = 8143,
+ unleashElements = 73680,
+ waterShield = 52127,
+ waterWalking = 546,
+ windShear = 57994,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ ancestralSwiftness = 16188,
+ astralShift = 108271,
+ -- earthShield = 974,
+ flametongueWeapon = 8024,
+ flameShock = 8050,
+ ghostWolf = 2645,
+ lightningShield = 324,
+ -- skyfury = 462854,
+ -- spiritwalkersGrace = 79206,
+ unleashFlame = 73683,
+ waterShield = 52127,
+ waterWalking = 546,
+ },
+ conduits = {
+ -- astralProtection = 337964,
+ },
+ covenants = {
+ -- chainHarvest = 320674,
+ -- faeTransfusion = 328923,
+ -- primordialWave = 326059,
+ -- vesperTotem = 324386,
+ },
+ debuffs = {
+ flameShock = 8050,
+ -- frostShock = 196840,
+ -- hex = 51514,
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+ -- chainsOfDevastation = 336735,
+ -- doomWinds = 335902,
+ -- elementalEquilibrium = 336730,
+ -- primalLavaActuators = 335895,
+ -- echoesOfGreatSundering = 336215,
+ -- seedsOfRampantGrowth = 356218,
+ -- skybreakersFieryDemise = 336734,
+ -- spiritwalkersTidalTotem = 335891,
+ -- deeptremorStone = 336739,
+ -- windspeakersLavaResurgence = 336063,
+ -- deeplyRootedElements = 336738,
+ },
+ talents = {
+ ancestralGuidance = 108281,
+ ancestralSwiftness = 16188,
+ astralShift = 108271,
+ callOfTheElements = 108285,
+ conductivity = 108282,
+ earthgrabTotem = 51485,
+ echoOfTheElements = 108283,
+ elementalBlast = 117014,
+ elementalMastery = 16166,
+ frozenPower = 63374,
+ naturesGuardian = 30884,
+ primalElementalist = 117013,
+ rushingStreams = 147074,
+ stoneBulwarkTotem = 108270,
+ totemicPersistence = 108284,
+ totemicProjection = 108287,
+ unleashedFury = 117012,
+ windwalkTotem = 108273,
+ },
+ talentsHeroic = {
+ -- -- Enhance / Elemental Stormbringer
+ -- unlimitedPower = 454391,
+ -- supercharge = 455110,
+ -- voltaicSurge = 454919,
+ -- awakeningStorms = 455129,
+ -- stormcaller = 454021,
+ -- stormSwell = 455088,
+ -- arcDischarge = 455096,
+ -- conductiveEnergy = 455123,
+ -- shockingGrasp = 454022,
+ -- rollingThunder = 454026,
+ -- naturesProtection = 454027,
+ -- surgingCurrents = 454372,
+ -- -- Enhance / Restoration Totemic
+ -- surgingTotem = 444995,
+ -- totemicRebound = 445025,
+ -- oversizedTotems = 445026,
+ -- swiftRecall = 445027,
+ -- imbuementMastery = 445028,
+ -- whirlingElements = 445024,
+ -- amplificationCore = 445029,
+ -- oversurge = 445030,
+ -- windBarrier = 445031,
+ -- pulseCapacitor = 445032,
+ -- supportiveImbuements = 445033,
+ -- livelyTotems = 445034,
+ -- reactivity = 445035,
+ -- totemicCoordination = 445036,
+ -- earthsurge = 455590,
+ -- -- Elemental / Restoration Farseer
+ -- callOfTheAncestors = 443450,
+ -- latentWisdom = 443449,
+ -- ancientFellowship = 443423,
+ -- offeringFromBeyond = 443451,
+ -- naturalHarmony = 443442,
+ -- earthenCommunion = 443441,
+ -- ancestralSwiftness = 443454,
+ -- heedMyCall = 443444,
+ -- routineCommunication = 443445,
+ -- primordialCapacity = 443448,
+ -- maelstromSupremacy = 443447,
+ -- elementalReverb = 443418,
+ -- spiritwalkersMomentum = 443425,
+ -- finalCalling = 443446,
+ },
+ totem = {
+ air = {
+ stormlashTotem = 120668,
+ windwalkTotem = 108273,
+ capacitorTotem = 108269,
+ groundingTotem = 8177,
+ },
+ earth = {
+ earthbindTotem = 2484,
+ earthElementalTotem = 2062,
+ earthquake = 61882,
+ tremorTotem = 8143,
+ },
+ fire = {
+ fireElementalTotem = 2894,
+ flameShock = 8050,
+ searingTotem = 3599,
+ magmaTotem = 8190,
+ },
+ water = {
+ healingStreamTotem = 5394,
+ healingTideTotem = 108280,
+ manaTideTotem = 16191,
+ },
+ },
+ },
+ },
+ WARLOCK = {
+ -- Affliction
+ [265] = {
+ abilities = {
+ agony = 980,
+ commanddemon = 119898,
+ corruption = 172,
+ darkSoul = 113860,
+ deathbolt = 264106,
+ demonicGateway = 311699,
+ drainLife = 234153,
+ drainSoul = 198590,
+ felDomination = 333889,
+ grimoireOfSacrifice = 108503,
+ haunt = 48181,
+ maleficRapture = 324536,
+ mortalCoil = 6789,
+ phantomSingularity = 205179,
+ reapSouls = 216698,
+ seedOfCorruption = 27243,
+ shadowBolt = 232670,
+ shadowBolt2 = 686,
+ shadowLock = 171138,
+ siphonLife = 63106,
+ spellLock = 19647,
+ spellLockgrimoire = 132409,
+ soulEffigy = 205178,
+ soulRot = 325640,
+ soulSwap = 386951,
+ soulTap = 387073,
+ summonDarkglare = 205180,
+ unstableAffliction = 30108,
+ vileTaint = 278350,
+ },
+ artifacts = {
+ },
+ buffs = {
+ cascadingCalamity = 275378,
+ compoundingHorror = 199281,
+ darkSoul = 113860,
+ deadwindHarvester = 216708,
+ demonicPower = 196099,
+ empoweredLifeTap = 235156,
+ felDomination = 333889,
+ nightfall = 264571,
+ tormentedSouls = 216695,
+ wrathOfConsumption = 199646,
+ inevitableDemise = 273525,
+ },
+ debuffs = {
+ agony = 980,
+ corruption = 146739,
+ haunt = 48181,
+ phantomSingularity = 205179,
+ seedOfCorruption = 27243,
+ siphonLife = 63106,
+ shadowEmbrace = 32390,
+ unstableAffliction = 316099,
+ unstableAffliction2 = 233496,
+ unstableAffliction3 = 233497,
+ unstableAffliction4 = 233498,
+ unstableAffliction5 = 233499,
+ vileTaint = 278350,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ absoluteCorruption = 196103,
+ creepingDeath = 264000,
+ darkCaller = 334183,
+ darkfury = 264874,
+ seedOfCorruption = 196226,
+ darkSoul = 113860,
+ demonicSacrifice = 108503,
+ drainSoul = 198590,
+ grimoireOfSacrifice = 108503,
+ haunt = 48181,
+ howlOfTerror = 5484,
+ inevitableDemise = 334319,
+ nightfall = 108558,
+ phantomSingularity = 205179,
+ siphonLife = 63106,
+ soulConduit = 215941,
+ sowTheSeeds = 196226,
+ vileTaint = 278350,
+ writheInAgony = 196102,
+ },
+ traits = {
+ cascadingCalamity = 275372,
+ dreadfulCalling = 278727,
+ inevitableDemise = 273521,
+ pandemicInvocation = 289364,
+ }
+ },
+ -- Demonology
+ [266] = {
+ abilities = {
+ axeToss = 89766,
+ bilescourgeBombers = 267211,
+ callDreadstalkers = 104316,
+ commandDemon = 119898,
+ corruption = 172,
+ demonbolt = 264178,
+ demonicEmpowerment = 193396,
+ demonicStrength = 267171,
+ demonwrath = 193440,
+ doom = 603,
+ drainLife = 234153,
+ felFirebolt = 104318,
+ felstorm = 89751,
+ grimoireFelguard = 111898,
+ guillotine = 386833,
+ handOfGuldan = 105174,
+ implosion = 196277,
+ netherPortal = 267217,
+ powerSiphon = 264130,
+ shadowBolt = 686,
+ shadowflame = 205181,
+ shadowLock = 171138,
+ shadowsBite = 387322,
+ spellLock = 19647,
+ summonDarkglare = 205180,
+ summonDemonicTyrant = 265187,
+ summonVilefiend = 264119,
+ thalkielsConsumption = 211714,
+ },
+ artifacts = {
+ thalkielsAscendance = 238145,
+ thalkielsConsumption = 211714,
+ },
+ buffs = {
+ demonicCalling = 205146,
+ demonicCore = 267102,
+ demonicEmpowerment = 193396,
+ demonicPower = 265273,
+ demonwrath = 193440,
+ explosivePotential = 275398,
+ forbiddenKnowledge = 278738,
+ netherPortal = 267218,
+ shadowsBite = 272945,
+ shadowyInspiration = 196269,
+ },
+ debuffs = {
+ doom = 603,
+ shadowflame = 205181,
+ corruption = 172,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ bilescourgeBombers = 267211,
+ darkfury = 264874,
+ demonicStrength = 267171,
+ demonicCalling = 205145,
+ doom = 603,
+ dreadlash = 264078,
+ fromTheShadows = 267170,
+ grimoireFelguard = 111898,
+ howlOfTerror = 5484,
+ innerDemons = 267216,
+ netherPortal = 267217,
+ powerSiphon = 264130,
+ sacrificedSouls = 267214,
+ summonVilefiend = 264119,
+ soulConduit = 215941,
+
+ },
+ traits = {
+ balefulInvocation = 287059,
+ explosivePotential = 275395,
+ shadowsBite = 272944,
+ }
+ },
+ -- Destruction
+ [267] = {
+ abilities = {
+ cataclysm = 152108,
+ channelDemonfire = 196447,
+ chaosBolt = 116858,
+ conflagrate = 17962,
+ corruption = 172,
+ commandDemon = 119898,
+ darkSoul = 113858,
+ devourMagic = 19505,
+ dimensionalRift = 196586,
+ drainLife = 234153,
+ felDomination = 333889,
+ grimoireOfSacrifice = 108503,
+ havoc = 80240,
+ immolate = 348,
+ incinerate = 29722,
+ rainOfFire = 5740,
+ shadowBolt = 686,
+ shadowburn = 17877,
+ shadowLock = 171138,
+ singeMagic = 119905,
+ singeMagicGrimoire = 132411,
+ soulFire = 6353,
+ spellLock = 19647,
+ spellLockgrimoire = 132409,
+ summonInfernal = 1122,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ backdraft = 117828, --196406,
+ crashingChaos = 277706,
+ darkSoul = 113858,
+ darkSoulInstability = 113858,
+ demonicPower = 196099,
+ empoweredLifeTap = 235156,
+ felDomination = 333889,
+ lessonsOfSpaceTime = 236174,
+ lordOfFlames = 224103,
+ },
+ debuffs = {
+ conflagrate = 265931,
+ eradication = 196414,
+ immolate = 157736,
+ havoc = 80240,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ cataclysm = 152108,
+ channelDemonfire = 196447,
+ darkFury = 264874,
+ darkSoul = 113858,
+ darkSoulInstability = 113858,
+ eradication = 196412,
+ flashover = 267115,
+ fireAndBrimstone = 196408,
+ grimoireOfSacrifice = 108503,
+ grimoireOfSupremacy = 266086,
+ howlOfTerror = 5484,
+ inferno = 270545,
+ internalCombustion = 266134,
+ reverseEntropy = 205148,
+ roaringBlaze = 205184,
+ shadowburn = 17877,
+ soulFire = 6353,
+ soulConduit = 215941,
+ },
+ traits = {
+ crashingChaos = 277644
+ }
+ },
+ -- Inital Warlock (1-10)
+ [1454] = {
+ abilities = {
+ controlDemon = 93375,
+ corruption = 172,
+ curseOfWeakness = 702,
+ drainLife = 234153,
+ shadowBolt = 686,
+ },
+ buffs = {
+
+ },
+ debuffs = {
+ corruption = 146739,
+ curseOfWeakness = 702,
+ drainLife = 234153,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ amplifyCurse = 328774,
+ banish = 710,
+ burningRush = 111400,
+ curseOfExhaustion = 334275,
+ curseOfTongues = 1714,
+ curseOfWeakness = 702,
+ createHealthstone = 6201,
+ createSoulwell = 29893,
+ commandDemon = 119898,
+ corruption = 172,
+ darkPact = 108416,
+ demonicGateway = 111771,
+ demonicCircle = 48018,
+ demonicTeleport = 48020,
+ devourMagic = 19505,
+ eyeOfKilrogg = 126,
+ felDomination = 333889,
+ fear = 5782,
+ grimoireFelhunter = 111897,
+ grimoireImp = 111859,
+ grimoireOfService = 108501,
+ grimoireSuccubus = 111896,
+ grimoireVoidwalker = 111895,
+ healthFunnel = 755,
+ inquisitorsGaze = 386344,
+ howlOfTerror = 5484,
+ lifeTap = 1454,
+ mortalCoil = 6789,
+ ritualOfDoom = 342601,
+ ritualOfSummoning = 698,
+ shadowflame = 384069,
+ shadowfury = 30283,
+ shadowBulwark = 119907,
+ spellLock = 19647,
+ seduction = 6358,
+ singeMagic = 89808,
+ soulburn = 385899,
+ soulHarvest = 196098,
+ soulstone = 20707,
+ summonSoulkeeper = 386256,
+ --summonDoomguard = 18540,
+ subjugateDemon = 1098,
+ summonFelguard = 30146,
+ summonFelhunter = 691,
+ summonFelImp = 688,
+ summonImp = 688,
+ summonInfernal = 1122,
+ summonSuccubus = 366222,
+ summonVoidwalker = 697,
+ summonWrathguard = 112870,
+ unendingBreath = 5697,
+ unendingResolve = 104773,
+ unstableaffliction = 316099,
+
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ burningRush = 111400,
+ demonicCircle = 48018,
+ demonicSynergy = 171982,
+ felDomination = 333889,
+ healthFunnel = 755,
+ grimoireOfSacrifice = 196099,
+ sindoreiSpite = 208871,
+ soulHarvest = 196098,
+ soulstone = 20707,
+ unendingBreath = 5697,
+ unendingResolve = 104773,
+ },
+ conduits = {
+ corruptingLeer = 339455,
+
+ },
+ covenants = {
+ decimatingBolt = 325289,
+ impendingCatastrophe = 321792,
+ scouringTithe = 312321,
+ },
+ debuffs = {
+ decimatingBolt = 325289,
+ impendingCatastrophe = 321792,
+ scouringTithe = 312321,
+ soulRot = 325640,
+ fear = 5782,
+ curseOfExhaustion = 334275,
+ curseOfTongues = 1714,
+ curseOfWeakness = 702,
+ },
+ glyphs = {
+ glyphOfTheFelImp = 219424,
+ },
+ talents = {
+ burningRush = 111400,
+ darkPact = 108416,
+ demonSkin = 219272,
+ mortalCoil = 6789,
+
+ },
+ },
+ },
+ WARRIOR = {
+ -- Arms
+ [71] = {
+ abilities = {
+ charge = 1250619,
+ colossusSmash = 86346,
+ dieByTheSword = 118038,
+ mortalStrike = 12294,
+ overpower = 7384,
+ slam = 1464,
+ sweepingStrikes = 12328,
+ whirlwind = 1680,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ -- ashenJuggernaut = 335234,
+ -- avatar = 389748,
+ -- battlelord = 346369,
+ -- battleStance = 386164,
+ -- bladestorm = 446035,
+ -- collateralDamage = 334783,
+ colossusSmash = 86346,
+ -- crushingAssault = 278826,
+ -- deadlyCalm = 262228,
+ -- defensiveStance = 386208,
+ -- exploiter = 335452,
+ -- imminentDemise = 445606,
+ -- inForTheKill = 215550,
+ -- juggernaut = 383290,
+ -- mercilessBonegrinder = 383316,
+ -- opportunist = 456120,
+ -- overpower = 400805,
+ -- recklessness = 389722,
+ -- stoneHeart = 225947,
+ -- suddenDeath = 52437,
+ suddenExecute = 139958,
+ sweepingStrikes = 12328,
+ tasteForBlood = 60503,
+ -- testOfMight = 275540,
+ },
+ conduits = {
+ -- ashenJuggernaut = 335232,
+ },
+ covenants = {
+ -- condemn = 317349,
+ -- condemnMassacre = 330334,
+ },
+ debuffs = {
+ colossusSmash = 108126,
+ -- deepWounds = 262115,
+ -- executionersPrecision = 272870,
+ -- markedForExecution = 445584,
+ -- hamstring = 1715,
+ -- ravager = 227876,
+ -- rend = 772,
+ },
+ glyphs = {
+ -- glyphOfThunderStrike = 68164,
+ },
+ talents = {
+ -- armoredToTheTeeth = 384124,
+ -- arterialBleed = 440995,
+ -- avatar = 107574,
+ -- barbaricTraining = 383082,
+ -- battleStance = 386164,
+ -- battlelord = 386630,
+ -- blademastersTorment = 390138,
+ -- bladestorm = 227847,
+ -- bloodborne = 383287,
+ -- bloodletting = 383154,
+ -- bloodsurge = 384361,
+ -- bluntInstruments = 383442,
+ -- boneshaker = 429639,
+ -- brutalFinish = 446085,
+ -- cleave = 845,
+ -- collateralDamage = 334779,
+ -- colossalMight = 429634,
+ -- colossusSmash = 167105,
+ -- criticalThinking = 389306,
+ -- crushingForce = 382764,
+ -- cullingCyclone = 444778,
+ -- danceOfDeath = 390713,
+ -- -- deadlyCalm = 262228,
+ -- deathDrive = 444770,
+ -- deftExperience = 389308,
+ -- demolish = 436358,
+ -- dieByTheSword = 118038,
+ -- dominanceOfTheColossus = 429636,
+ -- dreadnaught = 262150,
+ -- earthquaker = 440992,
+ -- enduranceTraining = 382940,
+ -- executionersPrecision = 386634,
+ -- exhilaratingBlows = 383219,
+ -- fatality = 383703,
+ -- fervorOfBattle = 202316,
+ -- fierceFollowthrough = 444773,
+ -- finishingBlows = 400205,
+ -- frothingBerserker = 392792,
+ -- fueledByViolence = 383103,
+ -- ignorePain = 190456,
+ -- imminentDemise = 444769,
+ -- impale = 383430,
+ -- improvedExecute = 316405,
+ -- improvedOverpower = 385571,
+ -- improvedSweepingStrikes = 383155,
+ -- inForTheKill = 248621,
+ -- juggernaut = 383292,
+ -- martialExpert = 429638,
+ -- martialProwess = 316440,
+ -- massacre = 281001,
+ -- mercilessBonegrinder = 383317,
+ -- mortalStrike = 12294,
+ -- mountainOfMuscleAndScars = 429642,
+ -- noStrangerToPain = 429644,
+ -- oneAgainstMany = 429637,
+ -- opportunist = 444774,
+ -- overpower = 7384,
+ -- overwhelmingBlades = 444772,
+ -- practicedStrikes = 429647,
+ -- preciseMight = 431548,
+ -- reapTheStorm = 444775,
+ -- relentlessPursuit = 444776,
+ -- rend = 772,
+ -- secondWind = 29838,
+ -- sharpenedBlades = 383341,
+ -- showNoMercy = 444771,
+ -- skullsplitter = 260643,
+ -- slayersDominance = 444767,
+ -- slayersMalice = 444779,
+ -- spitefulSerenity = 400314,
+ -- stormOfSwords = 385512,
+ -- stormWall = 388807,
+ -- strengthOfArms = 400803,
+ -- suddenDeath = 29725,
+ -- tactician = 184783,
+ -- testOfMight = 385008,
+ -- tideOfBattle = 429641,
+ -- twohandedWeaponSpecialization = 382896,
+ -- unhinged = 386628,
+ -- unrelentingOnslaught = 444780,
+ -- valorInVictory = 383338,
+ -- veteranVitality = 440993,
+ -- viciousAgility = 444777,
+ -- warMachine = 262231,
+ -- warbreaker = 262161,
+ -- warlordsTorment = 390140,
+ },
+ traits = {
+ -- seismicWave = 277639,
+ -- testOfMight = 275529,
+ }
+ },
+ -- Fury
+ [72] = {
+ abilities = {
+ crushingBlow = 335097,
+ enrage = 184361,
+ execute = 5308,
+ whirlwind = 190411,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ ashenJuggernaut = 392537,
+ avatar = 107574,
+ berserkerStance = 386196,
+ bladestorm = 446035,
+ bloodbath = 461288,
+ brutalFinish = 446918,
+ burstOfPower = 437121,
+ crushingBlow = 396752,
+ -- deathWish = 199261,
+ enrage = 184362,
+ enragedRegeneration = 184364,
+ -- frenzy = 335082,
+ meatCleaver = 85739,
+ -- mercilessBonegrinder = 346574,
+ opportunist = 456120,
+ recklessness = 1719,
+ suddenDeath = 280776,
+ -- whirlwind = 85739,
+ },
+ conduits = {
+
+ },
+ covenants = {
+ condemn = 317485,
+ condemnMassacre = 330325,
+ },
+ debuffs = {
+ championsMight = 386286,
+ markedForExecution = 445584,
+ odynsFuryTormentMh = 385060,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ armoredToTheTeeth = 384124,
+ ashenJuggernaut = 392536,
+ avatar = 107574,
+ avatarOfTheStorm = 437134,
+ barbaricTraining = 390674,
+ berserkerStance = 386196,
+ berserkersTorment = 390123,
+ bladestorm = 227847,
+ bloodborne = 385703,
+ bloodcraze = 393950,
+ bloodthirst = 23881,
+ brutalFinish = 446085,
+ burstOfPower = 437118,
+ coldSteelHotBlood = 383959,
+ crashingThunder = 436707,
+ criticalThinking = 383297,
+ cruelty = 392931,
+ crushingForce = 382764,
+ cullingCyclone = 444778,
+ dancingBlades = 391683,
+ deathDrive = 444770,
+ deftExperience = 383295,
+ depthsOfInsanity = 383922,
+ -- dragonRoar = 118000,
+ dualWieldSpecialization = 382900,
+ enduranceTraining = 391997,
+ enragedRegeneration = 184364,
+ fierceFollowthrough = 444773,
+ flashingSkies = 437079,
+ focusInChaos = 383486,
+ frenziedEnrage = 383848,
+ frenzy = 335077,
+ freshMeat = 215568,
+ frothingBerserker = 215571,
+ -- furiousCharge = 202224,
+ gatheringClouds = 436201,
+ groundCurrent = 436148,
+ hackAndSlash = 383877,
+ imminentDemise = 444769,
+ improvedBloodthirst = 383852,
+ improvedExecute = 316402,
+ improvedRagingBlow = 383854,
+ improvedWhirlwind = 12950,
+ invigoratingFury = 383468,
+ keepYourFeetOnTheGround = 438590,
+ lightningStrikes = 434969,
+ massacre = 206315,
+ meatCleaver = 280392,
+ odynsFury = 385059,
+ onslaught = 315720,
+ opportunist = 444774,
+ overwhelmingBlades = 444772,
+ powerfulEnrage = 440277,
+ ragingBlow = 85288,
+ rampage = 184367,
+ reapTheStorm = 444775,
+ recklessAbandon = 396749,
+ recklessness = 1719,
+ relentlessPursuit = 444776,
+ -- seethe = 335091,
+ showNoMercy = 444771,
+ -- siegebreaker = 280772,
+ singlemindedFury = 81099,
+ slaughteringStrikes = 388004,
+ slayersDominance = 444767,
+ slayersMalice = 444779,
+ snapInduction = 456270,
+ steadfastAsThePeaks = 434970,
+ stormBolts = 436162,
+ stormOfSteel = 382953,
+ stormShield = 438597,
+ strengthOfTheMountain = 437068,
+ suddenDeath = 280721,
+ swiftStrikes = 383459,
+ tenderize = 388933,
+ thorimsMight = 436152,
+ thunderBlast = 435607,
+ titansTorment = 390135,
+ titanicRage = 394329,
+ unbridledFerocity = 389603,
+ unhinged = 386628,
+ unrelentingOnslaught = 444780,
+ viciousAgility = 444777,
+ viciousContempt = 383885,
+ warMachine = 346002,
+ warpaint = 208154,
+ wrathAndFury = 392936,
+ },
+ traits = {
+ coldSteelHotBlood = 288080
+ }
+ },
+ -- Protection
+ [73] = {
+ abilities = {
+ demoralizingShout = 1160,
+ devastate = 20243,
+ focusedRage = 204488,
+ impendingVictory = 202168,
+ intercept = 198304,
+ lastStand = 12975,
+ ravager = 228920,
+ revenge = 6572,
+ shieldBlock = 2565,
+ shieldSlam = 23922,
+ shieldWall = 871,
+ shockwave = 46968,
+ thunderClap = 6343,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ avatar = 107574,
+ defensiveStance = 71,
+ lastStand = 12975,
+ revenge = 5302,
+ shieldBlock = 132404,
+ shieldWall = 871,
+ vengeanceFocusedRage = 202573,
+ vengeanceIgnorePain = 202574,
+ vengeanceRevenge = 202573,
+ victorious = 32216,
+ },
+ conduits = {
+
+ },
+ covenants = {
+ condemn = 317349,
+ },
+ debuffs = {
+ deepwoundsProt = 115767,
+ demoralizingShout = 1160,
+ thunderClap = 6343,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ bestServedCold = 202560,
+ warMachine = 316733,
+ bolster = 280001,
+ boomingVoice = 202743,
+ cracklingThunder = 203201,
+ devastator = 236279,
+ dragonRoar = 118000,
+ heavyRepercussions = 203177,
+ indomitable = 202095,
+ intoTheFray = 202603,
+ menace = 275338,
+ neverSurrender = 202561,
+ punish = 275334,
+ ravager = 228920,
+ rumblingEarth = 275339,
+ unstoppableForce = 275336,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ attack = 88163,
+ battleShout = 6673,
+ battleStance = 2457,
+ berserkerRage = 18499,
+ berserkerStance = 2458,
+ charge = 100,
+ cleave = 845,
+ commandingShout = 469,
+ defensiveStance = 71,
+ demoralizingBanner = 114203,
+ disarm = 676,
+ execute = 5308,
+ hamstring = 1715,
+ heroicLeap = 6544,
+ heroicStrike = 78,
+ heroicThrow = 57755,
+ intervene = 3411,
+ intimidatingShout = 5246,
+ mockingBanner = 114192,
+ pummel = 6552,
+ rallyingCry = 97462,
+ recklessness = 1719,
+ -- shieldBlock = 2565,
+ -- shieldSlam = 23922,
+ shatteringThrow = 64382,
+ shieldWall = 871,
+ skullBanner = 114207,
+ spellReflection = 23920,
+ sunderArmor = 7386,
+ taunt = 355,
+ throw = 122475,
+ thunderClap = 6343,
+ victoryRush = 34428,
+ -- whirlwind = 1680,
+ },
+ animas = {
+ -- bloodstainedWhetstone = 322036,
+ -- hurricaneHeart = 329122,
+ -- championsDecree = 322054,
+ -- periaptOfFuror = 322049,
+ -- pleonexianCommand = 329165,
+ -- rageMote = 322046,
+ -- weatheredRunestone = 349916,
+ -- singingStones = 329489,
+ -- singingStonesOfIntimidation = 329484,
+ -- singingStonesOfUnnerving = 329488,
+ -- tarnishedMedallion = 348483,
+ -- thunderingRoar = 322093,
+ -- zovaalsWarbanner = 322027,
+ -- bladedBulwark = 322026,
+ -- fanOfLongswords = 322029,
+ -- misshapenMirror = 329268,
+ -- shriekingFlagon = 329275,
+ -- voraciousCullingBlade = 329213,
+ -- kyrianWarhelm = 314290,
+ -- scratchedKnife = 322039,
+ -- signetOfTormentedKings = 329345,
+ -- smolderingInertia = 322032,
+ -- umbralEarTrumpet = 329283,
+ -- warlordsResolve = 322051,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ battleShout = 6673,
+ -- berserkerRage = 18499,
+ bloodbath = 12292,
+ defensiveStance = 71,
+ enrage = 12880,
+ -- firstStrike = 325381,
+ -- fujiedasFury = 207776,
+ -- ignorePain = 190456,
+ -- outburst = 364010,
+ recklessness = 1719,
+ -- seeingRed = 364006,
+ skullBanner = 114207,
+ -- smolderingInertia = 322032,
+ -- spellReflection = 23920,
+ victorious = 32216,
+ },
+ conduits = {
+ -- viciousContempt = 337302,
+ -- destructiveReverberations = 339939,
+ },
+ covenants = {
+ -- ancientAftershock = 325886,
+ -- conquerorsBanner = 324143,
+ -- fleshcraft = 324631,
+ -- spearOfBastion = 307865,
+ },
+ debuffs = {
+ deepWounds = 115768,
+ disarm = 116095,
+ -- markOfFyralath = 414532,
+ },
+ glyphs = {
+
+ },
+ items = {
+ -- treacherousTransmitter = 221023,
+ },
+ runeforges = {
+ -- battlelord = 335274,
+ -- cadenceOfFujieda = 335555,
+ -- deathmaker = 335567,
+ -- elysianMight = 357996,
+ -- enduringBlow = 335458,
+ -- exploiter = 335451,
+ -- leaper = 335214,
+ -- misshapenMirror = 335253,
+ -- recklessDefence = 335582,
+ -- reprisal = 335718,
+ -- seismicReverberation = 335758,
+ -- signetOfTormentedKings = 335266,
+ -- sinfulSurge = 354131,
+ -- theWall = 335239,
+ -- thunderlord = 335229,
+ -- unhinged = 335282,
+ -- willOfTheBerserker = 335594,
+ -- unbreakableWill = 335629,
+ },
+ talents = {
+ avatar = 107574,
+ bladestorm = 46924,
+ bloodbath = 12292,
+ doubleTime = 103827,
+ disruptingShout = 102060,
+ dragonRoar = 118000,
+ enragedRegeneration = 55694,
+ impendingVictory = 103840,
+ juggernaut = 103826,
+ massSpellReflection = 114028,
+ piercingHowl = 12323,
+ safeguard = 114029,
+ secondWind = 29838,
+ shockwave = 46968,
+ staggeringShout = 107566,
+ stormBolt = 107570,
+ vigilance = 114030,
+ warbringer = 103828,
+ },
+ },
+ },
+ -- Global
+ Shared = {
+ Shared = {
+ abilities = {
+ autoAttack = 6603,
+ autoShot = 75,
+ giftOfTheNaaru = br.functions.spell:getRacial("Draenei"), --select(7, GetSpellInfo(GetSpellInfo(28880))),
+ global = 5176, -- Classic uses class-specific GCD spells, but we use a default here
+ -- latentArcana = 296971,
+ -- lightsJudgment = 255647,
+ quakingPalm = br.functions.spell:getRacial("Pandaren"),
+ racial = br.functions.spell:getRacial(),
+ shadowmeld = br.functions.spell:getRacial("NightElf"),
+ -- twoForms = 68996,
+ },
+ animas = {
+ -- scrollOfElchaver = 305006,
+ },
+ artifacts = {
+ -- artificialDamage = 226829,
+ -- artificialStamina = 211309,
+ -- concordanceOfTheLegionfall = 239042,
+ },
+ buffs = {
+ -- ancientHysteria = 90355,
+ -- battlePotionOfAgility = 279152,
+ -- battlePotionOfIntellect = 279151,
+ -- battlePotionOfStrength = 279153,
+ -- superiorBattlePotionOfAgility = 298146,
+ -- potionOfUnbridledFury = 300714,
+ -- blessingOfSacrifice = 6940,
+ -- greaterBlessingOfKings = 203538,
+ -- greaterBlessingOfWisdom = 203539,
+ -- battleScarredAugmentation = 270058, -- BfA Augment Rune Buff
+ -- blessingOfFreedom = 1044,
+ -- blessingOfProtection = 1022,
+ -- bloodLust = {
+ -- ancientHysteria = 90355,
+ -- bloodlust = 2825,
+ -- drumsOfRage = 146555,
+ -- -- drumsOfTheMaelstrom = 256740,
+ -- -- drumsOfTheMountain = 230935,
+ -- heroism = 32182,
+ -- -- netherwinds = 160452,
+ -- -- primalRage = 264667,
+ -- timewarp = 80353,
+ -- },
+ -- concordanceOfTheLegionfall = 239042,
+ -- defiledAugmentation = 224001, -- Lightforged Augment Rune buff
+ -- felFocus = 242551,
+ -- flashOfSpringBlossoms = 105689,
+ -- flaskOfTenThousandScars = 188035,
+ -- flaskOfTheCountlessArmies = 188034,
+ -- flaskOfTheSeventhDemon = 188033,
+ -- flaskOfTheWhisperedPact = 188031,
+ -- flaskOfTheCurrents = 251836,
+ -- flaskOfEndlessFathoms = 251837,
+ -- flaskOfTheVastHorizon = 251838,
+ -- flaskOfTheUndertow = 251839,
+ -- flayedWingToxin = 345546,
+ -- fruitfulMachinatins = 242623, -- Absorb Shield from Deceiver's Grand Design
+ -- mistcallerOcarina = 330067, -- SL Trinket with group buff
+ -- greaterFlaskOfEndlessFathoms = 298837,
+ -- greaterFlaskOfTheCurrents = 298836,
+ -- greaterFlaskOfTheUndertow = 298841,
+ -- greaterFlaskOfTheVastHorizon = 298839,
+ -- guidingHand = 242622, -- from The Deceiver's Grand Design
+ -- heroism = 32182,
+ -- ineffableTruth = 316801,
+ -- latentArcana = 296962,
+ -- netherwinds = 160452,
+ -- norgannonsForesight = 236373,
+ -- overchargeMana = 299624,
+ -- potionOfBurstingBlood = 265514,
+ -- potionOfFocusedResolve = 298317,
+ -- prolongedPower = 229206,
+ racial = br.functions.spell:getRacial(),
+ -- razorCoral = 303570, -- Crit Buff from Ashvane's Razor Coral
+ -- sephuz1 = 208051, -- the fulltime 10% movement, 2% haste buff
+ -- sephuz2 = 208052, -- the proc, 70% movement, 25% haste buff
+ -- sephuzCooldown = 226262, -- CD (30 seconds) for the proc
+ -- shadowmeld = 20580,
+ -- symbolOfHope = 64901,
+ -- timeWarp = 80353,
+ -- whispersOfInsanity = 176151,
+ -- cracklingTourmaline = 290372,
+ -- saphireofBrilliance = 290365,
+ -- vigorEngaged = 287916,
+ -- soulshape = 310143,
+ -- soulIgnition = 345251,
+ -- spectralFlaskOfPower = 307185,
+ -- elementalPotionOfPower = 371024,
+ -- elementalPotionOfUltimatePower = 371028,
+ -- phialOfGlacialFury = 373257,
+ -- phialOfTepidVersatility = 371172,
+ -- icedPhialOfCorruptingRage = 374000,
+ -- domineeringArrogance = 411661,
+ -- runeOfReorigination = 139120,
+ -- vicious = 148903,
+ -- virmensBite = 105697,
+ },
+ --TODO Add to API
+ itemEnchantments = {
+ --Dragonflight weapon Imbue's from inscription enchantments
+ --Quality of aura should always be GOLD,SILVER,COPPER
+ -- buzzingRune = { 6514, 6513, 6512 },
+ -- chirpingRune = { 6695, 6694, 6515 },
+ -- howlingRune = { 6518, 6517, 6516 },
+ -- hissingRune = { 6839, 6837, 6838 },
+ },
+ conduits = {
+
+ },
+ covenants = {
+ -- covenantAbility = 313347,
+ -- doorOfShadows = 300728,
+ -- fleshcraft = 324631,
+ -- soulshape = 310143,
+ -- flicker = 324701,
+ -- summonSteward = 324739,
+ },
+ debuffs = {
+ -- bloodOfTheEnemy = 297108,
+ -- concentratedFlame = 295368,
+ -- conductiveInk = 302565,
+ -- convert = 122740,
+ -- dampening = 110310,
+ -- eyeOfLeotheras = 206649,
+ -- necroticWound = 209858,
+ -- razorCoral = 303568, --304877,
+ -- repeatPerformance = 304409,
+ -- shiverVenom = 301624,
+ -- temptation = 234143,
+ -- eyeOfCorruption = 315161,
+ -- grandDelusions = 319695,
+ -- graspingTendrils = 315176,
+ -- creepingMadness = 313255,
+ -- fixate = 318078,
+ -- thirstForBlood = 266107,
+ -- mightyBash = 5211,
+ -- defiledGround = 314565,
+ -- vileCorruption = 314392,
+ -- timeToFeed = 162415, -- Oshir Iron Docks
+ -- cascadingTerror = 314478,
+ },
+ essences = {
+ },
+ },
+ },
+}
diff --git a/System/Lists/Expansions/MOP/Spells.lua b/System/Lists/Expansions/MOP/Spells.lua
new file mode 100644
index 0000000000..ba093a5531
--- /dev/null
+++ b/System/Lists/Expansions/MOP/Spells.lua
@@ -0,0 +1,4632 @@
+local _, br = ...
+if br.lists == nil then br.lists = {} end
+
+-- Racial spell IDs for MoP (original races; no Allied Races).
+br.lists.racials = {
+ -- Alliance
+ Dwarf = 20594, -- Stoneform
+ Gnome = 20589, -- Escape Artist
+ Human = 59752, -- Every Man for Himself
+ NightElf = 20580, -- Shadowmeld
+ Worgen = 68992, -- Darkflight
+ -- Horde
+ Goblin = 69041, -- Rocket Barrage
+ Tauren = 20549, -- War Stomp
+ Troll = 26297, -- Berserking
+ Scourge = 7744, -- Will of the Forsaken
+ -- Both
+ Pandaren = 107079, -- Quaking Palm
+ -- Dynamic lookup base IDs
+ _bloodElfBase = 69179,
+ _draeneiBase = 28880,
+ _orcBase = 33702,
+}
+
+br.lists.spells = {
+ DEATHKNIGHT = {
+ -- Blood
+ [250] = {
+ abilities = {
+ deathsCaress = 195292,
+ },
+ artifacts = {
+ allConsumingRot = 192464,
+ bloodFeast = 192548,
+ bonebreaker = 192538,
+ coagulopathy = 192460,
+ consumption = 205223,
+ danceOfDarkness = 192514,
+ grimPerseverance = 192447,
+ ironHeart = 192450,
+ meatShield = 192453,
+ mouthOfHell = 192570,
+ rattlingBones = 192557,
+ sanguinaryAffinity = 221775,
+ skeletalShattering = 192558,
+ theHungeringMaw = 214903,
+ umbilicusEternus = 193213,
+ unendingThirst = 192567,
+ veinrender = 192457,
+ },
+ buffs = {
+ boneShield = 195181,
+ bloodShield = 77535,
+ coagulopathy = 391477,
+ crimsonScourge = 81136,
+ dancingRuneWeapon = 81256,
+ hemostasis = 273947,
+ ossuary = 219786,
+ runeTap = 194679,
+ tombstone = 219809,
+ vampiricBlood = 55233,
+ vampiricStrength = 408356,
+ },
+ debuffs = {
+ asphyxiate = 221562,
+ blooddrinker = 206931,
+ heartStrike = 206930,
+ markOfBlood = 206940,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ antimagicBarrier = 205727,
+ bloodBoil = 50842,
+ bloodFeast = 391386,
+ bloodTap = 221699,
+ blooddrinker = 206931,
+ bloodiedBlade = 458753,
+ bloodshot = 391398,
+ bloodworms = 195679,
+ boneCollector = 458572,
+ bonestorm = 194844,
+ carnage = 458752,
+ coagulopathy = 391477,
+ consumption = 274156,
+ dancingRuneWeapon = 49028,
+ everlastingBond = 377668,
+ foulBulwark = 206974,
+ gorefiendsGrasp = 108199,
+ gripOfTheDead = 273952,
+ heartStrike = 206930,
+ heartbreaker = 221536,
+ heartrend = 377655,
+ hemostasis = 273946,
+ improvedBoneShield = 374715,
+ improvedHeartStrike = 374717,
+ improvedVampiricBlood = 317133,
+ insatiableBlade = 377637,
+ ironHeart = 391395,
+ leechingStrike = 377629,
+ markOfBlood = 206940,
+ marrowrend = 195182,
+ ossifiedVitriol = 458744,
+ ossuary = 219786,
+ perseveranceOfTheEbonBlade = 374747,
+ purgatory = 114556,
+ rapidDecomposition = 194662,
+ redThirst = 205723,
+ reinforcedBones = 374737,
+ relishInBlood = 317610,
+ runeTap = 194679,
+ sanguineGround = 391458,
+ shatteringBone = 377640,
+ tighteningGrasp = 206970,
+ tombstone = 219809,
+ umbilicusEternus = 391517,
+ vampiricBlood = 55233,
+ voracious = 273953,
+ willOfTheNecropolis = 206967,
+ },
+ },
+ -- Frost
+ [251] = {
+ abilities = {
+ -- empowerRuneWeapon = 47568,
+ frostStrike = 49143,
+ -- frostwyrmsFury = 279302,
+ howlingBlast = 49184,
+ obliterate = 49020,
+ pillarOfFrost = 51271,
+ -- remorselessWinter = 196770,
+ soulReaper = 130735,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ -- breathOfSindragosa = 155166,
+ -- coldHeart = 281210,
+ -- darkSuccor = 101568,
+ -- empowerRuneWeapon = 47568,
+ -- eradicatingBlow = 337936,
+ -- frozenPulse = 195750,
+ -- icyCitadel = 272723,
+ -- icyTalons = 194879,
+ killingMachine = 51124,
+ pillarOfFrost = 51271,
+ -- remorselessWinter = 196770,
+ rime = 59052,
+ -- unleashedFrenzy = 338501,
+ },
+ conduits = {
+ -- eradicatingBlow = 337934,
+ -- everfrost = 337988,
+ -- unleashedFrenzy = 338492,
+ },
+ debuffs = {
+ -- breathOfSindragosa = 155166,
+ -- remorselessWinter = 196771,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ -- asphyxiate = 108194,
+ -- avalanche = 207142,
+ -- breathOfSindragosa = 152279,
+ -- coldHeart = 281208,
+ -- frostscythe = 207230,
+ -- frozenPulse = 194909,
+ -- gatheringStorm = 194912,
+ -- glacialAdvance = 194913,
+ -- hornOfWinter = 57330,
+ -- hypothermicPresence = 321995,
+ -- icecap = 207126,
+ -- inexorableAssault = 253593,
+ -- murderousEffeciency = 207061,
+ -- obliteration = 281238,
+ },
+ traits = {
+ -- frozenTempest = 278487,
+ -- icyCitadel = 272718,
+ }
+ },
+ -- Unholy
+ [252] = {
+ abilities = {
+ -- apocalypse = 275699,
+ -- armyOfTheDead = 42650,
+ -- darkTransformation = 63560,
+ -- epidemic = 207317,
+ -- festeringStrike = 85948,
+ -- outbreak = 77575,
+ -- scourgeStrike = 55090,
+ -- -- Pet Abilities
+ -- claw = 47468,
+ -- gnaw = 47481,
+ -- huddle = 47484,
+ -- hook = 212468,
+ -- leap = 47482,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ -- corpseShield = 207319,
+ -- darkSuccor = 101568,
+ -- darkTransformation = 63560,
+ -- festermight = 274373,
+ -- huddle = 91838,
+ -- necrosis = 207346,
+ -- soulReaper = 215711,
+ -- suddenDoom = 49530,
+ -- runicCorruption = 51460,
+ -- unholyAssault = 207289,
+ -- unholyBlight = 115989,
+ -- empowerRuneWeapon = 47568,
+ -- commanderOfTheDead = 390259,
+ -- summonGargoyle = 61777,
+ },
+ conduits = {
+ -- convocationOfTheDead = 338553,
+ },
+ debuffs = {
+ -- festeringWound = 194310,
+ -- unholyBlight = 115994,
+ -- virulentPlague = 191587,
+ -- rottenTouch = 390275,
+ -- deathRot = 377537,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ -- allWillServe = 194916,
+ -- armyOfTheDamned = 276837,
+ -- burstingSores = 207264,
+ -- clawingShadows = 207311,
+ -- defile = 152280,
+ -- ebonFever = 207269,
+ -- gripOfTheDead = 273952,
+ -- harbingerOfDoom = 276023,
+ -- infectedClaws = 207272,
+ -- pestilence = 277234,
+ -- summonGargoyle = 49206,
+ -- unholyAssault = 207289,
+ -- unholyBlight = 115989,
+ -- unholyPact = 319230,
+ -- wraithWalk = 212552,
+ -- commanderOfTheDead = 390259,
+ -- deathRot = 377537,
+ -- improvedDeathCoil = 377580,
+ -- coilOfDevastation = 390270,
+ -- festermight = 377590,
+ -- rottenTouch = 390275,
+ -- vileContagion = 390279,
+ },
+ traits = {
+ -- magusOfTheDead = 288417,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ antiMagicShell = 48707,
+ armyOfTheDead = 42650,
+ bloodBoil = 48721,
+ bloodPlague = 55078,
+ bloodPresence = 48263,
+ bloodStrike = 45902,
+ chainsOfIce = 45524,
+ controlUndead = 111673,
+ darkSimulacrum = 77606,
+ deathAndDecay = 43265,
+ deathCoil = 47541,
+ deathGate = 50977,
+ deathGrip = 49576,
+ deathStrike = 49998,
+ empowerRuneWeapon = 47568,
+ frostFever = 55095,
+ frostPresence = 48266,
+ hornOfWinter = 57330,
+ iceboundFortitude = 48792,
+ icyTouch = 45477,
+ mindFreeze = 47528,
+ necroticStrike = 73975,
+ onAPaleHorse = 51986,
+ outbreak = 77575,
+ pathOfFrost = 3714,
+ pestilence = 50842,
+ plagueStrike = 45462,
+ raiseAlly = 61999,
+ raiseDead = 46584,
+ strangulate = 47476,
+ unholyPresence = 48265,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ -- antiMagicShell = 48707,
+ bloodCharge = 114851,
+ -- coldHeartItem = 235599,
+ darkSuccor = 101568,
+ -- deathAndDecay = 188290,
+ -- deathsDue = 324164,
+ frostPresence = 48266,
+ -- iceboundFortitude = 48792,
+ -- icyTalons = 194878,
+ lichborne = 49039,
+ -- pathOfFrost = 3714,
+ -- unholyStrength = 53365,
+ -- wraithWalk = 212552,
+ },
+ covenants = {
+ -- abominationLimb = 315443,
+ -- deathsDue = 324128,
+ -- shackleTheUnworthy = 312202,
+ -- swarmingMist = 311648,
+ },
+ debuffs = {
+ bloodPlague = 55078,
+ -- chainsOfIce = 45524,
+ -- controlUndead = 111673,
+ -- darkCommand = 56222,
+ -- frostBreath = 190780,
+ frostFever = 55095,
+ -- razorice = 51714,
+ -- soulReaper = 343294,
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+ -- bitingCold = 334678,
+ -- deadliestCoil = 334949,
+ -- phearomones = 335177,
+ -- superstrain = 334974,
+ },
+ talents = {
+ antiMagicZone = 51052,
+ asphyxiate = 108194,
+ bloodTap = 45529,
+ chilblains = 50041,
+ conversion = 119975,
+ deathPact = 48743,
+ deathSiphon = 108196,
+ deathsAdvance = 96268,
+ desecratedGround = 108201,
+ gorefiendsGrasp = 108199,
+ lichborne = 49039,
+ plagueLeech = 123693,
+ purgatory = 114556,
+ remorselessWinter = 108200,
+ roilingBlood = 108170,
+ runicCorruption = 51462,
+ runicEmpowerment = 81229,
+ unholyBlight = 115989,
+ },
+ talentsHeroic = {
+ -- -- Deathbringer
+ -- bindInDarkness = 440031,
+ -- bloodFever = 440002,
+ -- darkTalons = 436687,
+ -- deathsMessenger = 437122,
+ -- expellingShield = 439948,
+ -- exterminate = 441378,
+ -- grimReaper = 434905,
+ -- pactOfTheDeathbringer = 440476,
+ -- painfulDeath = 443564,
+ -- reapersMark = 439843,
+ -- runeCarvedPlates = 440282,
+ -- soulRupture = 437161,
+ -- swiftEnd = 443560,
+ -- waveOfSouls = 439851,
+ -- witherAway = 441894,
+ -- -- Rider of the Apocalypse
+ -- aFeastOfSouls = 444072,
+ -- apocalypseNow = 444040,
+ -- deathCharge = 444010,
+ -- furyOfTheHorsemen = 444069,
+ -- horsemansAid = 444074,
+ -- hungeringThirst = 444037,
+ -- mawswornMenace = 444099,
+ -- mograinesMight = 444047,
+ -- nazgrimsConquest = 444052,
+ -- onAPalerHorse = 444008,
+ -- pactOfTheApocalypse = 444083,
+ -- ridersChampion = 444005,
+ -- trollbanesIcyFury = 444097,
+ -- whitemanesFamine = 444033,
+ -- -- Sanlayn
+ -- bloodsoakedGround = 434033,
+ -- bloodyFortitude = 434136,
+ -- frenziedBloodthirst = 434075,
+ -- giftOfTheSanlayn = 434152,
+ -- inciteTerror = 434151,
+ -- inflictionOfSorrow = 434143,
+ -- newlyTurned = 433934,
+ -- pactOfTheSanlayn = 434261,
+ -- sanguineScent = 434263,
+ -- theBloodIsLife = 434260,
+ -- vampiricAura = 434100,
+ -- vampiricSpeed = 434028,
+ -- vampiricStrike = 433901,
+ -- visceralStrength = 434157,
+ }
+ },
+ },
+ DRUID = {
+ -- Initial
+ [1447] = {
+
+ },
+ -- Balance
+ [102] = {
+ abilities = {
+ celestialAlignment = 194223,
+ fullMoon = 274283,
+ furyOfElune = 202770,
+ halfMoon = 274282,
+ innervate = 29166,
+ starfire = 194153,
+ newMoon = 274281,
+ removeCorruption = 2782,
+ solarBeam = 78675,
+ typhoon = 132469,
+ wrath = 190984,
+ starfall = 191034,
+ starsurge = 78674,
+ sunfire = 93402,
+ stellarFlare = 202347,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ blessingOfElune = 202737,
+ blessingOfAnshe = 202739,
+ incarnationChoseOfElune = 102560,
+ celestialAlignment = 194223,
+ eclipse_lunar = 48518,
+ eclipse_solar = 48517,
+ onethsOverconfidence = 209407,
+ onethsIntuition = 209406,
+ solarEmpowerment = 164545,
+ innervate = 29166,
+ lunarEmpowerment = 164547,
+ warriorOfElune = 202425,
+ balanceForm = 24858,
+ stellarDrift = 202461,
+ emeraldDreamcatcher = 208190,
+ owlkinFrenzy = 157228,
+ powerOfEluneTheMoonGoddness = 208284,
+ sephuzSecret = 208052,
+ astralAcceleration = 242232,
+ newMoonController = -2027671,
+ solstice = 343648,
+ starfall = 191034,
+ starLord = 279709, --backwards compatible
+ starlord = 279709,
+ livelySpirit = 279279,
+ arcanicPulsar = 287790,
+ primordialArcanicPulsar = 338668,
+ balanceOfAllThings = 339942,
+ balanceOfAllThingsNature = 339943, -- Nature Buff
+ balanceOfAllThingsArcane = 339946, -- Arcane Buff
+ timewornDreambinder = 339949,
+ kindredEmpowerment = 327022,
+ onethsClearVision = 338661,
+ ravenousFrenzy = 323546,
+
+ },
+ conduits = {
+ preciseAlignment = 340706,
+
+
+ },
+ covenants = {
+ loneEmpowerment = 338142,
+ ravenousFrenzy = 323546,
+ empowerBond = 326446,
+ },
+ debuffs = {
+ stellarFlare = 202347,
+ moonfire = 164812,
+ sunfire = 164815,
+ stellarEmpowerment = 197637,
+ },
+ glyphs = {
+ },
+ runeforges = {
+ balanceOfAllThings = 339942,
+ lycarasFleetingGlimpse = 340059,
+ primordialArcanicPulsar = 338668,
+ timewornDreambinder = 339949,
+
+ },
+ talents = {
+ aetherialKindling = 327541,
+ astralCommunion = 202359,
+ astralSmolder = 394058,
+ balanceOfAllThings = 394049,
+ bloomingInfusion = 429433,
+ bounteousBloom = 429215,
+ celestialAlignment = 194223,
+ cenariusMight = 455797,
+ controlOfTheDream = 434249,
+ convokeTheSpirits = 323764,
+ denizenOfTheDream = 394076,
+ dreamSurge = 433831,
+ durabilityOfNature = 429227,
+ earlySpring = 428937,
+ eclipse = 79577,
+ elunesGuidance = 393991,
+ expansiveness = 429399,
+ forceOfNature = 205636,
+ furyOfElune = 202770,
+ grovesInspiration = 429402,
+ harmonyOfTheGrove = 428731,
+ incarnationChosenOfElune = 394013,
+ lightOfTheSun = 202918,
+ lunarShrapnel = 393868,
+ naturesBalance = 202430,
+ naturesGrace = 393959,
+ newMoon = 274281,
+ orbitBreaker = 383197,
+ potentEnchantments = 429420,
+ powerOfGoldrinn = 394046,
+ powerOfNature = 428859,
+ powerOfTheDream = 434220,
+ protectiveGrowth = 433748,
+ radiantMoonlight = 394121,
+ rattleTheStars = 393954,
+ shootingStars = 202342,
+ solarBeam = 78675,
+ solstice = 343647,
+ soulOfTheForest = 114107,
+ starfall = 191034,
+ starlord = 202345,
+ starweaver = 393940,
+ stellarFlare = 202347,
+ sunderedFirmament = 294108,
+ treantsOfTheMoon = 428544,
+ twinMoons = 279620,
+ umbralEmbrace = 393760,
+ umbralIntensity = 383195,
+ waningTwilight = 393956,
+ warriorOfElune = 202425,
+ wildMushroom = 88747,
+ },
+ traits = {
+ arcanicPulsar = 287773,
+ dawningSun = 276152,
+ highNoon = 278505,
+ lunarShrapnel = 278507,
+ powerOfTheMoon = 273367,
+ streakingStars = 272871,
+ sunblaze = 274397,
+ }
+ },
+ -- Feral
+ [103] = {
+ abilities = {
+ -- adaptiveSwarmDamage = 391889, -- Not Castable but needed for In-Flight
+ -- adaptiveSwarmHeal = 391891, -- Not Castable but needed for In-Flight
+ berserk = 106951,
+ maim = 22570,
+ -- moonfireCat = 155625,
+ naturesSwiftness = 132158,
+ pounce = 9005,
+ ravage = 6785,
+ removeCorruption = 2782,
+ rip = 1079,
+ savageRoar = 52610,
+ shred = 5221,
+ skullBash = 106839,
+ survivalInstincts = 61336,
+ swipe = 62078,
+ thrash = 106830,
+ },
+ buffs = {
+ berserk = 106952,
+ clearcasting = 135700,
+ dreamOfCenarius = 145152,
+ feralFury = 144865,
+ feralRage = 146874,
+ incarnation = 102543,
+ naturesSwiftness = 132158,
+ predatorySwiftness = 69369,
+ savageRoar = 52610,
+ survivalInstincts = 61336,
+ },
+ debuffs = {
+ pounce = 9005,
+ rip = 1079,
+ thrash = 106830,
+ },
+ items = {
+ },
+ talents = {
+ cenarionWard = 102351,
+ disorientingRoar = 99,
+ displacerBeast = 102280,
+ dreamOfCenarius = 108373,
+ faerieSwarm = 106707,
+ felineSwiftness = 131768,
+ forceOfNature = 106737,
+ heartOfTheWild = 108288,
+ incarnation = 106731,
+ massEntanglement = 102359,
+ mightyBash = 5211,
+ naturesVigil = 124974,
+ renewal = 108238,
+ soulOfTheForest = 114107,
+ typhoon = 132469,
+ ursolsVortex = 102793,
+ wildCharge = 102401,
+ yserasGift = 145108,
+ },
+ },
+ -- Guardian
+ [104] = {
+ abilities = {
+ berserk = 50334,
+ incapacitatingRoar = 99,
+ incarnationGuardianOfUrsoc = 102558,
+ maul = 6807,
+ removeCorruption = 2782,
+ skullBash = 106839,
+ stampedingRoar = 106898,
+ survivalInstincts = 61336,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ berserk = 50334,
+ galacticGuardian = 213708,
+ goryFur = 201671,
+ incarnationGuardianOfUrsoc = 102558,
+ gore = 93622,
+ pulverize = 158792,
+ savageCombatant = 340613,
+ survivalInstincts = 61336,
+ toothAndClaw = 135286,
+ },
+ conduits = {
+ savageCombatant = 340609,
+
+ },
+ covenants = {
+ loneProtection = 338018,
+ },
+ debuffs = {
+ moonfireGuardian = 164812,
+ toothAndClaw = 135601,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ afterTheWildfire = 371905,
+ aggravateWounds = 441829,
+ berserkPersistence = 377779,
+ berserkRavage = 343240,
+ berserkUncheckedAggression = 377623,
+ bestialStrength = 441841,
+ bloodFrenzy = 203962,
+ brambles = 203953,
+ bristlingFur = 155835,
+ circleOfLifeAndDeath = 391969,
+ clawRampage = 441835,
+ convokeTheSpirits = 323764,
+ dreadfulWound = 441809,
+ dreamOfCenarius = 372119,
+ earthwarden = 203974,
+ elunesFavored = 370586,
+ empoweredShapeshifting = 441689,
+ flashingClaws = 393427,
+ fountOfStrength = 441675,
+ -- frontOfThePack = 377835,
+ furyOfNature = 370695,
+ galacticGuardian = 203964,
+ gore = 210706,
+ goryFur = 200854,
+ guardianOfElune = 213680,
+ improvedSurvivalInstincts = 328767,
+ incarnationGuardianOfUrsoc = 102558,
+ infectedWounds = 345208,
+ innateResolve = 377811,
+ killingStrikes = 441824,
+ layeredMane = 279552,
+ mangle = 231064,
+ maul = 6807,
+ packsEndurance = 441844,
+ pulverize = 80313,
+ rageOfTheSleeper = 200851,
+ ravage = 441583,
+ reinforcedFur = 393618,
+ reinvigoration = 372945,
+ rendAndTear = 204053,
+ ruthlessAggression = 441814,
+ scintillatingMoonlight = 238049,
+ soulOfTheForest = 158477,
+ strikeForTheHeart = 441845,
+ survivalInstincts = 61336,
+ survivalOfTheFittest = 203965,
+ tearDownTheMighty = 441846,
+ toothAndClaw = 135288,
+ twinMoonfire = 372567,
+ untamedSavagery = 372943,
+ -- ursineAdept = 300346,
+ ursocsEndurance = 393611,
+ ursocsFury = 372505,
+ ursocsGuidance = 393414,
+ viciousCycle = 371999,
+ vulnerableFlesh = 372618,
+ wildpowerSurge = 441691,
+ wildshapeMastery = 441678,
+ },
+ traits = {
+ layeredMane = 279552,
+ twistedClaws = 275906,
+ },
+ },
+ -- Restoration
+ [105] = {
+ abilities = {
+ efflorescence = 145205,
+ innervate = 29166,
+ ironbark = 102342,
+ lifebloom = 33763,
+ naturesCure = 88423,
+ nourish = 50464,
+ revitalize = 212040,
+ wrath = 5176,
+ sunfire = 93402,
+ tranquility = 740,
+ typhoon = 132469,
+ yserasGift = 145108,
+ naturesSwiftness = 132158,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ abundance = 207640,
+ cenarionWard = 102352,
+ clearcasting = 16870,
+ incarnationTreeOfLife = 33891,
+ innervate = 29166,
+ groveTending = 279793,
+ rejuvenationGermination = 155777,
+ lifebloom = 33763,
+ eclipse_lunar = 48518,
+ eclipse_solar = 48517,
+ --lunarEmpowerment = 164547,
+ regrowth = 8936,
+ --solarEmpowerment = 164545,
+ soulOfTheForest = 114108,
+ cultivat = 200389,
+ tranquility = 157982,
+ springblossom = 207386,
+ symbolOfHope = 64901,
+ fullbloom = 290213,
+ heartOfTheWild = 108291,
+ },
+ conduits = {
+ loneMeditation = 338035,
+ },
+ debuffs = {
+ moonfire = 164812,
+ sunfire = 164815,
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+ theDarkTitansLesson = 338831,
+ verdantInfusion = 338829,
+
+ },
+ talents = {
+ abundance = 207383,
+ balanceAffinity = 197632,
+ bloomingInfusion = 429433,
+ bondWithNature = 439929,
+ bounteousBloom = 429215,
+ buddingLeaves = 392167,
+ burstingGrowth = 440120,
+ callOfTheElderDruid = 426784,
+ cenarionWard = 102351,
+ cenariusGuidance = 393371,
+ cenariusMight = 455797,
+ controlOfTheDream = 434249,
+ cultivation = 200390,
+ dreamSurge = 433831,
+ durabilityOfNature = 429227,
+ earlySpring = 428937,
+ entanglingVortex = 439895,
+ expansiveness = 429399,
+ feralAffinity = 197490,
+ flourish = 197721,
+ flowerWalk = 439901,
+ germination = 155675,
+ grovesInspiration = 429402,
+ guardianAffinity = 197491,
+ harmoniousConstitution = 440116,
+ harmonyOfTheGrove = 428731,
+ huntBeneathTheOpenSkies = 439868,
+ implant = 440118,
+ incarnationTreeOfLife = 33891,
+ innerPeace = 197073,
+ lethalPreservation = 455461,
+ nourish = 50464,
+ overgrowth = 203651,
+ photosynthesis = 274902,
+ potentEnchantments = 429420,
+ powerOfNature = 428859,
+ powerOfTheDream = 434220,
+ protectiveGrowth = 433748,
+ resilientFlourishing = 439880,
+ rootNetwork = 439882,
+ soulOfTheForest = 158478,
+ springBlossoms = 207385,
+ strategicInfusion = 439890,
+ thrivingGrowth = 439528,
+ treantsOfTheMoon = 428544,
+ twinSprouts = 440117,
+ vigorousCreepers = 440119,
+ wildstalkersPower = 439926,
+ },
+ traits = {
+ dawningSun = 276152,
+ highNoon = 278505,
+ }
+ },
+ -- All
+ Shared = {
+ abilities = {
+ aquaticForm = 1066,
+ barkskin = 22812,
+ bearForm = 5487,
+ catForm = 768,
+ charmWoodlandCreature = 127757, -- Tome of the Wilds
+ cyclone = 33786,
+ dash = 1850,
+ -- dreamwalk = 193753,
+ entanglingRoots = 339,
+ faerieFire = 770,
+ ferociousBite = 22568,
+ -- flap = 164862,
+ flightForm = 33943,
+ growl = 6795,
+ healingTouch = 5185,
+ innervate = 29166,
+ mangle = 33917,
+ mangleBear = 33878,
+ maul = 6807,
+ markOfTheWild = 1126,
+ moonfire = 8921,
+ -- mountForm = 210053,
+ prowl = 5215,
+ rake = 1822,
+ rebirth = 20484,
+ regrowth = 8936,
+ rejuvenation = 774,
+ revive = 50769,
+ shred = 5221,
+ soothe = 2908,
+ -- sunfire = 164815,
+ symbiosis = 110309,
+ swiftFlightForm = 40120,
+ -- swipe = 213764,
+ -- swipeBear = 213771,
+ -- swipeCat = 106785,
+ teleportMoonglade = 18960,
+ thrashBear = 77758,
+ tigersFury = 5217,
+ -- trackBeasts = 210065,
+ trackHumanoids = 5225,
+ travelForm = 783,
+ wrath = 5176,
+ },
+ animas = {
+ -- lycarasBargin = 329960,
+ -- lycarasTwig = 330666,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ aquaticForm = 1066,
+ barkskin = 22812,
+ bearForm = 5487,
+ burningEssence = 138927,
+ catForm = 768,
+ dash = 1850,
+ faerieFire = 770,
+ flightForm = 33943,
+ frenziedRegeneration = 22842,
+ -- heartOfTheWild = 319454,
+ innervate = 29166,
+ -- ironfur = 192081,
+ -- kindredEmpowerment = 327139,
+ -- kindredSpiritsBuff = 326967, --This is buff on ourselves...at least as dps
+ -- kindredEmpowermentEnergize = 327022, --/kindred-empowerment this is when someone else cast it on you
+ -- loneSpirit = 338041,
+ -- lycarasTwig = 330668, -- Anima Power Torghast
+ markOfTheWild = 1126,
+ -- moonkinForm = 197625,
+ -- onethsPerception = 339800,
+ prowl = 5215,
+ rejuvenation = 774,
+ regrowth = 8936,
+ shadowmeld = 58984,
+ -- soulshape = 310143,
+ -- stagForm = 210053,
+ stampedingRoar = 106898,
+ stampedingRoarCat = 77764,
+ -- suddenAmbush = 340698,
+ swiftFlightForm = 40120,
+ -- tigerDash = 252216,
+ tigersFury = 5217,
+ travelForm = 783,
+ treantForm = 114282,
+ wildGrowth = 48438,
+ legacyOfTheEmperor = 117666, -- Monk Shared Buff
+ blessingOfKings = 20217, -- Paladin Shared Buff
+ },
+ conduits = {
+ -- deepAllegiance = 341378,
+ -- tasteForBlood = 340682,
+ },
+ covenants = {
+ -- adaptiveSwarm = 325727,
+ -- adaptiveSwarmHeal = 325748,
+ -- convokeTheSpiritsCovenant = 323764,
+ -- kindredSpirits = 326434,
+ -- ravenousFrenzy = 323546,
+ },
+ debuffs = {
+ -- adaptiveSwarm = 325733,
+ cyclone = 33786,
+ entanglingRoots = 339,
+ growl = 6795,
+ hibernate = 2637,
+ -- lycarasBargin = 329961,
+ massEntanglement = 102359,
+ moonfire = 8921,
+ rake = 1822,
+ rip = 1079,
+ -- thrashBear = 192090,
+ weakenedArmor = 113746,
+ },
+ glyphs = {
+ -- glyphOfTheCheetah = 131113,
+ -- glyphOfTheDoe = 224122,
+ -- glyphOfTheFeralChameleon = 210333,
+ -- glyphOfTheOrca = 114333,
+ -- glyphOfTheSentinel = 219062,
+ -- glyphOfTheUrsolChameleon = 107059,
+ },
+ runeforges = {
+ -- apexPredatorsCraving = 339139,
+ -- eyeOfFearfullSymmetry = 339141,
+ -- frenzyband = 340053,
+ },
+ talents = {
+ },
+ talentsHeroic = {
+ -- bloomingInfusion = 429433,
+ -- bounteousBloom = 429215,
+ -- burstingGrowth = 440120,
+ -- cenariusMight = 455797,
+ -- controlOfTheDream = 434249,
+ -- dreamSurge = 433831,
+ -- durabilityOfNature = 429227,
+ -- earlySpring = 428937,
+ -- expansiveness = 429399,
+ -- grovesInspiration = 429402,
+ -- harmonyOfTheGrove = 428731,
+ -- potentEnchantments = 429420,
+ -- powerOfNature = 428859,
+ -- powerOfTheDream = 434220,
+ -- protectiveGrowth = 433748,
+ -- resilientFlourishing = 439880,
+ -- rootNetwork = 439882,
+ -- treantsOfTheMoon = 428544,
+ },
+ traits = {
+ -- livelySpirit = 279642,
+ },
+ },
+ },
+ HUNTER = {
+ -- BeastMastery
+ [253] = {
+ abilities = {
+ aspectOfTheWild = 193530,
+ barbedShot = 217200,
+ bestialWrath = 19574,
+ cobraShot = 193455,
+ concussiveShot = 5116,
+ counterShot = 147362,
+ intimidation = 19577,
+ killCommand = 34026,
+ killShot = 53351,
+ multishot = 2643,
+ },
+ animas = {
+ piercingScope = 331183,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ aspectOfTheWild = 193530,
+ barbedShot = 217200,
+ beastCleave = 118455,
+ berserking = 26297,
+ bestialWrath = 19574,
+ danceOfDeath = 274443,
+ direBeast = 120694,
+ frenzy = 272790,
+ parselsTongue = 248084,
+ spittingCobra = 194407,
+ volley = 194386,
+ },
+ debuffs = {
+ barbedShot = 217200,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ animalCompanion = 267116,
+ barrage = 120360,
+ bloodshed = 321530,
+ direBeast = 120679,
+ killCleave = 378207,
+ killerCobra = 199532,
+ killerInstinct = 273887,
+ oneWithThePack = 199528,
+ scentOfBlood = 193532,
+ stampede = 201430,
+ stomp = 199530,
+ thrillOfTheHunt = 257944,
+ wailingArrow = 392060,
+ },
+ traits = {
+ danceOfDeath = 274441,
+ primalInstincts = 279806,
+ rapidReload = 278530,
+ },
+ },
+ -- Marksmanship
+ [254] = {
+ abilities = {
+ },
+ artifacts = {
+ },
+ buffs = {
+ bulletstorm = 389020,
+ bullseye = 204090,
+ deadEye = 321461,
+ deathblow = 378770,
+ feignDeath = 5384,
+ inTheRhythm = 272733,
+ lethalShots = 260395,
+ lockAndLoad = 194594,
+ masterMarksman = 269576,
+ preciseShots = 260242,
+ razorFragments = 388993,
+ salvo = 400456,
+ steadyFocus = 193534,
+ trickShots = 257622,
+ trueshot = 288613,
+ unerringVision = 274447,
+ },
+ debuffs = {
+ huntersMark = 257284,
+ serpentSting = 271788,
+ },
+ glyphs = {
+
+ },
+ interrupts = {
+ counterShot = 147362,
+ },
+ talents = {
+ aimedShot = 19434,
+ barrage = 120360,
+ bindingShackles = 321468,
+ bulletstorm = 389019,
+ bullseye = 204089,
+ callingTheShots = 260404,
+ carefulAim = 260228,
+ chimaeraShot = 342049,
+ crackShot = 321293,
+ deathblow = 378769,
+ eagletalonsTrueFocus = 389449,
+ explosiveShot = 212431,
+ fanTheHammer = 459794,
+ focusedAim = 378767,
+ heavyAmmo = 378910,
+ hydrasBite = 260241,
+ improvedSteadyShot = 321018,
+ inTheRhythm = 407404,
+ killShot = 53351,
+ killZone = 459921,
+ killerAccuracy = 378765,
+ legacyOfTheWindrunners = 406425,
+ lightAmmo = 378913,
+ lockAndLoad = 194595,
+ loneWolf = 155228,
+ masterMarksman = 260309,
+ multishot = 257620,
+ nightHunter = 378766,
+ penetratingShots = 459783,
+ preciseShots = 260240,
+ rapidFire = 257044,
+ rapidFireBarrage = 459800,
+ razorFragments = 384790,
+ readiness = 389865,
+ salvo = 400456,
+ serpentstalkersTrickery = 378888,
+ smallGameHunter = 459802,
+ steadyFocus = 193533,
+ streamline = 260367,
+ surgingShots = 391559,
+ tacticalReload = 400472,
+ trickShots = 257621,
+ trueshot = 288613,
+ unerringVision = 386878,
+ volley = 260243,
+ wailingArrow = 459806,
+ },
+ traits = {
+ focusedFire = 278531,
+ inTheRhythm = 264198,
+ rapidReload = 278530,
+ steadyAim = 277651,
+ surgingShots = 287707,
+ unerringVision = 274444,
+ },
+ },
+ -- Survival
+ [255] = {
+ abilities = {
+ aspectOfTheEagle = 186289,
+ carve = 187708,
+ coordinatedAssault = 266779,
+ harpoon = 190925,
+ intimidation = 19577,
+ killCommand = 259489,
+ killShot = 320976,
+ muzzle = 187707,
+ pheromoneBomb = 270323, -- Wildfire Infusion
+ raptorStrike = 186270,
+ serpentSting = 259491,
+ shrapnelBomb = 270335, -- Wildfire Infusion
+ volatileBomb = 271045, -- Wildfire Infusion
+ wakeUp = 210000,
+ wildfireBomb = 259495,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ aspectOfTheEagle = 186289,
+ blurOfTalons = 277969,
+ coordinatedAssault = 360952,
+ exposedFlank = 252094, -- Tier 21
+ mongooseFury = 259388,
+ tipOfTheSpear = 260286,
+ vipersVenom = 268552,
+ madBombardier = 363805, -- Tier 28
+ },
+ debuffs = {
+ bloodseeker = 259277,
+ internalBleeding = 270343,
+ serpentSting = 259491,
+ shrapnelBomb = 270339,
+ wildfireBomb = 269747,
+ pheromoneBomb = 270332,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ alphaPredator = 269737,
+ birdsOfPrey = 260331,
+ bloodseeker = 260248,
+ butchery = 212436,
+ crackShot = 321293,
+ flankingStrike = 269751,
+ furyOfTheEagle = 203415,
+ guerrillaTactics = 264332,
+ hydrasBite = 260241,
+ mongooseBite = 259387,
+ steelTrap = 162488,
+ termsOfEngagement = 265895,
+ tipOfTheSpear = 260285,
+ vipersVenom = 268501,
+ wildfireInfusion = 271014,
+ },
+ traits = {
+ blurOfTalons = 277653,
+ latentPoison = 273283,
+ primevalIntuition = 288570,
+ upCloseAndPersonal = 278533,
+ venomousFangs = 274590,
+ wildernessSurvival = 279589,
+ }
+ },
+ -- All
+ Shared = {
+ abilities = {
+ -- Hunter Abilities
+ arcaneShot = 185358,
+ aspectOfTheChameleon = 61648,
+ aspectOfTheCheetah = 186257,
+ aspectOfTheTurtle = 186265,
+ disengage = 781,
+ eagleEye = 6197,
+ exhilaration = 109304,
+ eyeOfTheBeast = 321297,
+ feignDeath = 5384,
+ flare = 1543,
+ fortitudeOfTheBear = 392956,
+ freezingTrap = 187650,
+ huntersMark = 257284,
+ steadyShot = 56641,
+ trackBeasts = 1494,
+ trackDemons = 19878,
+ trackDragonkin = 19879,
+ trackElementals = 19880,
+ trackGiants = 19882,
+ trackHidden = 19885,
+ trackHumanoids = 19883,
+ trackMechanicals = 229533,
+ trackUndead = 19884,
+ wingClip = 195645,
+ -- Pet Management
+ beastLore = 1462,
+ callPet = 9,
+ callPet1 = 883,
+ callPet2 = 83242,
+ callPet3 = 83243,
+ callPet4 = 83244,
+ callPet5 = 83245,
+ commandPet = 272651,
+ dismissPet = 2641,
+ feedPet = 6991,
+ fetch = 125050,
+ mendPet = 136,
+ playDead = 209997,
+ revivePet = 982,
+ tameBeast = 1515,
+ wakeUp = 210000,
+ ----------------------------
+ -- Pet - Basic Abilities ---
+ ----------------------------
+ bite = 17253, -- Basalisk, Bat, Beetle, Blood Beast, Boar, Carapid, Carrion Bird, Chimera, Core Hound, Crocolisk, Devilsaur, Direhorn, Dragonhawk, Feathermane, Fox, Hound, Hydra, Hyena, Lizard, Mechanical, Pterrordax, Ravager, Ray, Riverbeast, Serpent, Shale Beast, Spider, Stone Hound, Turtle, Warp Stalker, Water Strider, Wind Serpent, Wolf, Worm
+ claw = 16827, -- Aqiri, Bear, Bird of Prey, Cat, Crab, Raptor, Rodent, Scorpid, Spirit Beast, Tallstrider
+ dash = 61684, -- Speed Increase (ALL PETS)
+ smack = 49966, -- Camel, Clefthoof, Courser, Crane, Gorilla, Gruffhorn, Mammoth, Monkey, Moth, Oxen, Scalehide, Sporebat, Stag, Toad, Wasp
+ growl = 2649, -- Taunt (ALL PETS)
+ ---------------------------
+ -- Pet - Spec Abilities ---
+ ---------------------------
+ primalRage = 264667, -- Ferocity Dmg Cd
+ -- survivalOfTheFittest = 264735, -- Tenacity Def Cd
+ mastersCall = 53271, -- Cunning Freedom
+ ------------------------------
+ -- Pet - Special Abilities ---
+ ------------------------------
+ -- AOE
+ burrowAttack = 93433, -- Worm
+ froststormBreath = 92380, -- Chimera - Channeled Cone AOE
+ -- Defense/Dodge
+ agileReflexes = 160011, -- Fox - Dodge
+ bristle = 263869, -- Boar - Defensive
+ bulwark = 279410, -- Carapid - Defensive
+ catlikeReflexes = 263892, -- Cat - Dodge
+ defenseMatrix = 263868, -- Mechanical - Defensive
+ dragonsGuile = 263887, -- Dragonhawk - Dodge
+ featherFlurry = 263916, -- Feathermane - Dodge
+ fleethoof = 341117, -- Courser - Dodge
+ hardenCarapace = 90339, -- Beetle - Defensive
+ obsidianSkin = 263867, -- Core Hound - Defensive
+ primalAgility = 160044, -- Monkey - Dodge
+ scaleShield = 263865, -- Scalehide - Defensive
+ serpentSwiftness = 263904, -- Serpent - Dodge
+ shellShield = 26064, -- Turtle - Defensive
+ solidShell = 160063, -- Shale Beast - Defensive
+ swarmOfFlies = 279336, -- Toad - Dodge
+ wingedAgility = 264360, -- Wind Serpent - Dodge
+ -- Dispel
+ chiJiTranq = 344350, -- Crane
+ naturesGrace = 344352, -- Stag
+ netherEnergy = 344349, -- Ray -- netherEnergy
+ serenityDust = 344353, -- Moth
+ sonicScreech = 344348, -- Bat -- sonicScreech
+ soothingWater = 344346, -- Water Strider
+ spiritPulse = 344351, -- Spirit Beast -- spiritPulse
+ sporeCloud = 344347, -- Sporebat
+ -- Heal
+ eternalGuardian = 267922, -- Stone Hound - Ressurect
+ feast = 159953, -- Devilsaur - Heal (req dead humanoid/beast nearby)
+ spiritmend = 90361, -- Spirit Beast - Heal
+ -- Mortal Wounds Debuff
+ acidBite = 263863, -- Hydra - Mortal Wounds Debuff
+ bloodyScreech = 24423, -- Carrion Bird - Mortal Wounds Debuff
+ deadlySting = 160060, -- Scorpid - Mortal Wounds Debuff
+ gnaw = 263856, -- Rodent - Mortal Wounds Debuff
+ gore = 263861, -- Direhorn - Mortal Wounds Debuff
+ grievousBite = 279362, -- Lizard - Mortal Wounds Debuff
+ gruesomeBite = 160018, -- Riverbeast - Mortal Wounds Debuff
+ infectedBite = 263853, -- Hyena - Mortal Wounds Debuff
+ monsterousBite = 54680, -- Devilsaur - Mortal Wounds Debuff
+ ravage = 263857, -- Ravager - Mortal Wounds Debuff
+ savageRend = 263854, -- Savage Rend - Mortal Wounds Debuff
+ toxicSting = 263858, -- Wasp - Mortal Wounds Debuff
+ -- Slow
+ acidSpit = 263446, -- Worm - Slow
+ ankleCrack = 50433, -- Crocolisk - Slow
+ bloodBolt = 288962, -- Blood Beast - Slow
+ dustCloud = 50285, -- Tallstrider - Slow
+ frostBreath = 54644, -- Chimera - Slow
+ furiousBite = 263840, -- Wolf - Slow
+ lockJaw = 263423, -- Hound - Slow
+ petrifyingGaze = 263841, -- Basilisk - Slow
+ pin = 50245, -- Crab - Slow
+ talonRend = 263852, -- Bird of Prey - Slow
+ tendonRip = 160065, -- Aqiri - Slow
+ trample = 341118, -- Mammoth - Slow
+ warpTime = 35346, -- Warp Stalker - Slow
+ webSpray = 160067, -- Spider - Slow
+ -- Slow Fall
+ updraft = 160007, -- Feathermane/Pterrordax - Slow Fall
+ -- Stealth
+ prowl = 24450, -- Stealth
+ spiritWalk = 90328, -- Spirit Beast - Stealth
+ -- Tricks / Play / Rest
+ play = 90347, -- Fox
+ restBear = 94019, -- Bear
+ restRodent = 126364, -- Rodent
+ trickCrane = 126259, -- Crane
+ trickBirdOfPrey = 94022, -- Bird of Prey
+ -- Water Walking
+ surfaceTrot = 126311, -- Water Strider - Water Walking
+ },
+ animas = {
+ soulforgeEmbers = 331197,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ aspectOfTheTurtle = 186265,
+ eagletalonsTrueFocus = 336851,
+ feignDeath = 5384,
+ flayersMark = 324156, -- Covenant
+ killingFrenzy = 363665, -- T28 4pc
+ mendPet = 136,
+ nesingwarysTrappingApparatus = 336744,
+ playDead = 209997,
+ prowl = 24450,
+ resonatingArrow = 356263, -- Covenant
+ spiritWalk = 90328,
+ surfaceTrot = 126311, -- Water Strider - Water Walking
+ updraft = 160007, -- Feathermane/Pterrordax - Slow Fall
+ volley = 260243,
+ wildSpirits = 328231,
+ },
+ conduits = {
+ bloodletting = 341440,
+ marksmansAdvantage = 339264,
+ },
+ covenants = {
+ flayedShot = 324149,
+ resonatingArrow = 308491,
+ wildSpirits = 328231,
+ },
+ debuffs = {
+ bestialFerocity = 191413,
+ freezingTrap = 3355,
+ huntersMark = 257284,
+ intimidation = 24934,
+ latentPoison = 378015,
+ mortalWounds = 115804, -- Pet Ability Debuff
+ resonatingArrow = 308498, -- Covenant
+ soulforgeEmbers = 331269, -- Covenant
+ tarTrap = 135299,
+ wildMark = 328275, -- Covenant
+ },
+ glyphs = {
+
+ },
+ interrupts = {
+ freezingTrap = 187650,
+ },
+ runeforges = {
+ bagOfMunitions = 356264,
+ eagletalonsTrueFocus = 336849,
+ nesingwarysTrappingApparatus = 336743,
+ pouchOfRazorFragments = 356618,
+ rylakstalkersConfoundingStrikes = 336901,
+ secretsOfTheUnblinkingVigil = 336878,
+ serpentstalkersTrickery = 336870,
+ soulforgeEmbers = 336745,
+ surgingShots = 336867,
+ qaplaEredunWarOrder = 336830,
+ },
+ talents = {
+ bindingShot = 109248,
+ blackrockMunitions = 462036,
+ bornToBeWild = 266921,
+ burstingShot = 186387,
+ camouflage = 199483,
+ concussiveShot = 5116,
+ counterShot = 147362,
+ devilsaurTranquilizer = 459991,
+ disruptiveRounds = 343244,
+ emergencySalve = 459517,
+ entrapment = 393344,
+ ghillieSuit = 459466,
+ highExplosiveTrap = 236776,
+ huntersAvoidance = 384799,
+ implosiveTrap = 462031,
+ improvedKillShot = 343248,
+ improvedTraps = 343247,
+ intimidation = 19577,
+ keenEyesight = 378004,
+ kindlingFlare = 459506,
+ kodoTranquilizer = 459983,
+ loneSurvivor = 388039,
+ misdirection = 34477,
+ momentOfOpportunity = 459488,
+ naturalMending = 270581,
+ noHardFeelings = 459546,
+ paddedArmor = 459450,
+ pathfinding = 378002,
+ posthaste = 109215,
+ quickLoad = 378771,
+ rejuvenatingWind = 385539,
+ roarOfSacrifice = 53480,
+ scareBeast = 1513,
+ scatterShot = 213691,
+ scoutsInstincts = 459455,
+ scrappy = 459533,
+ serratedTips = 459502,
+ survivalOfTheFittest = 264735,
+ specializedArsenal = 459542,
+ tarTrap = 187698,
+ tarcoatedBindings = 459460,
+ territorialInstincts = 459507,
+ trailblazer = 199921,
+ tranquilizingShot = 19801,
+ triggerFinger = 459534,
+ unnaturalCauses = 459527,
+ wildernessMedicine = 343242,
+ },
+ talentsHeroic = {
+ -- Dark Ranger
+ blackArrow = 430703,
+ darkChains = 430712,
+ darkEmpowerment = 430718,
+ deathShade = 430711,
+ darknessCalls = 430722,
+ embraceTheShadows = 430704,
+ graveReaper = 430719,
+ overshadow = 430716,
+ shadowErasure = 430720,
+ shadowHounds = 430707,
+ shadowLash = 430717,
+ shadowSurge = 430714,
+ smokeScreen = 430709,
+ witheringFire = 430715,
+ -- Pack Leader
+ beastOfOpportunity = 445700,
+ corneredPrey = 445702,
+ coveringFire = 445715,
+ cullTheHerd = 445717,
+ denRecovery = 445710,
+ frenziedTear = 445696,
+ furiousAssault = 445699,
+ howlOfThePack = 445707,
+ packAssault = 445721,
+ packCoordination = 445505,
+ scatteredPrey = 445768,
+ tirelessHunt = 445701,
+ viciousHunt = 445404,
+ wildAttacks = 445708,
+ -- Sentinel
+ catchOut = 451516,
+ crescentSteel = 451530,
+ extrapolatedShots = 450374,
+ eyesClosed = 450381,
+ dontLookBack = 450373,
+ invigoratingPulse = 450379,
+ lunarStorm = 450385,
+ overwatch = 450384,
+ releaseAndReload = 450376,
+ sentinel = 450369,
+ sentinelPrecision = 450375,
+ sentinelWatch = 451546,
+ sideline = 450378,
+ symphonicArsenal = 450383,
+ },
+ },
+ },
+ MAGE = {
+ -- Arcane
+ [62] = {
+ abilities = {
+ alterTime = 342245,
+ arcaneBarrage = 44425,
+ arcaneBlast = 30451,
+ arcaneFamiliar = 205022,
+ arcaneMissiles = 5143,
+ arcaneOrb = 153626,
+ arcanePower = 12042,
+ chargedUp = 205032,
+ conjuremanaGem = 759,
+ displacement = 212801,
+ erosion = 205039,
+ evocation = 12051,
+ -- fireblast = 519836,
+ greaterInvisibility = 110959,
+ markOfAluneth = 224968,
+ netherTempest = 114923,
+ presenceofMind = 205025,
+ prismaticBarrier = 235450,
+ runeofPower = 116011,
+ slow = 31589,
+ supernova = 157980,
+ touchOfTheMagi = 321507,
+ },
+ artifacts = {
+ aegwynnsAscendance = 187680,
+ aegwynnsFury = 187287,
+ aegwynnsInperative = 187264,
+ aegwynnsIntensity = 238054,
+ aegwynnsWrath = 187321,
+ alunethsAvarice = 238090,
+ ancientPower = 214917,
+ arcanePurification = 187313,
+ arcaneRebound = 188006,
+ blastingRod = 187258,
+ cracklingEnergy = 187310,
+ etherealSensitivity = 187276,
+ everywhereAtOnce = 187301,
+ forceBarrier = 210716,
+ intensityOfTheTirisgarde = 241121,
+ markOfAluneth = 224968,
+ mightOfTheGuardians = 187318,
+ ruleOfThrees = 215463,
+ sloooowDown = 210730,
+ timeAndSpace = 238126,
+ torrentialBarrage = 187304,
+ touchOfTheMagi = 210725,
+ },
+ buffs = {
+ arcaneCharge = 36032,
+ arcaneFamiliar = 210126,
+ arcanePower = 12042,
+ -- arcaneCharge = 36032,
+ evocation = 12051,
+ expandedPotential = 327495,
+ arcaneMissles = 79683,
+ presenceOfMind = 205025,
+ prismaticBarrier = 235450,
+ rhoninsAssaultingArmwraps = 208081,
+ runeofPower = 116011,
+ ruleOfThrees = 264774,
+ brainStorm = 273330,
+ clearcasting = 263725,
+ },
+ debuffs = {
+ arcaneCharge = 36032,
+ netherTempest = 114923,
+ touchoftheMagi = 210824,
+
+
+ },
+ glyphs = {
+
+ },
+ talents = {
+ arcaneEcho = 342231,
+ amplification = 236628,
+ arcaneFamiliar = 205022,
+ arcaneOrb = 153626,
+ chronoShift = 235711,
+ mastersOfTime = 342249,
+ --erosion = 205039,
+ netherTempest = 114923,
+ overpowered = 155147,
+ resonance = 205028,
+ reverberate = 281482,
+ rulesOfThrees = 264354,
+ slipstream = 236457,
+ supernova = 157980,
+ timeAnomaly = 210805
+ --temporalFlux = 234302,
+ --wordsOfPower = 205035,
+ },
+ traits = {
+ anomalousImpact = 279867,
+ arcanePressure = 274594,
+ arcanePummeling = 270669,
+ brainStorm = 273326,
+ explosiveEcho = 278537,
+ galvanizingSpark = 278536,
+ },
+ },
+ -- Fire
+ [63] = {
+ abilities = {
+ alterTime = 108978,
+ blazingBarrier = 235313,
+ blastWave = 157981,
+ cinderstorm = 198929,
+ combustion = 190319,
+ dragonsBreath = 31661,
+ disciplinaryCommand = 327365,
+ fireball = 133,
+ fireBlast = 108853,
+ fireBlast2 = 319836,
+ flameOn = 205029,
+ flamestrike = 2120,
+ infernalCascade = 336821,
+ livingBomb = 44457,
+ meteor = 153561,
+ phoenixFlames = 257541,
+ pyroblast = 11366,
+ shiftingPower = 314791,
+ scorch = 2948,
+ soulIgnition = 345211,
+ },
+ artifacts = {
+ aftershocks = 194431,
+ phoenixReborn = 215773,
+ },
+ buffs = {
+ blazingBarrier = 235313,
+ blasterMaster = 274598,
+ combustion = 190319,
+ disciplinaryArcane = 327369,
+ disciplinaryCommand = 327365,
+ disciplinaryCommandFrost = 327366,
+ disciplinaryCommandFire = 327368,
+ firestorm = 333097,
+ heatingUp = 48107,
+ hotStreak = 48108,
+ iceFloes = 108839,
+ infernalCascade = 336832,
+ kaelthasUltimateAbility = 209455,
+ preheat = 273333,
+ pyroclasm = 269651,
+ shiftingPower = 314791,
+ sunKingsBlessings = 333313,
+ soulIgnition = 345211,
+ },
+ debuffs = {
+ ignite = 12654,
+ meteorBurn = 155158,
+ cauterized = 87024,
+
+ },
+ glyphs = {
+
+ },
+ talents = {
+ alexstraszasFury = 235870,
+ blastWave = 157981,
+ blazingSoul = 235365,
+ --cinderstorm = 198929,
+ conflagration = 205023,
+ --controlledBurn = 205033,
+ freneticSpeed = 236058,
+ firestarter = 205026,
+ flameOn = 205029,
+ flamePatch = 205037,
+ kindling = 155148,
+ livingBomb = 44457,
+ meteor = 153561,
+ focusMagic = 321358,
+ fromTheAshes = 342344,
+ pyromaniac = 205020,
+ pyroclasm = 269650,
+ searingTouch = 269644,
+ },
+ covenants = {
+ shiftingPower = 314791,
+ },
+ traits = {
+ preheat = 273332,
+ blasterMaster = 274596,
+ },
+ conduits = {
+ infernalCascade = 336821,
+ },
+ runeforges = {
+ firestorm = 333097,
+ grislyIcicle = 333393,
+ disciplinaryCommand = 327365,
+ sunKingsBlessings = 333313
+ }
+ },
+ -- Frost
+ [64] = {
+ abilities = {
+ alterTime = 108978,
+ blizzard = 190356,
+ coldSnap = 235219,
+ coneOfCold = 120,
+ --fireBlast = 108853, - Fire Mage version, Frost should not have this and already listed fireBlast below
+ flurry = 44614,
+ freeze = 231596,
+ frostbolt = 116,
+ frostBomb = 112948,
+ frozenOrb = 84714,
+ frozenTouch = 205030,
+ fireBlast = 319836,
+ iceBarrier = 11426,
+ iceFloes = 108839,
+ iceForm = 198144,
+ iceLance = 30455,
+ iceNova = 157997,
+ icyVeins = 12472,
+ petFreeze = 33395,
+ rayOfFrost = 205021,
+ runeofPower = 116011,
+ removeCurse = 475,
+ summonWaterElemental = 31687,
+ waterbolt = 31707,
+ },
+ artifacts = {
+ -- icyHand = 220817,
+ },
+ buffs = {
+ brainFreeze = 190446,
+ concentratedCoolness = 198148,
+ freezingRain = 270232,
+ frostBomb = 112948,
+ fingersOfFrost = 44544,
+ icyVeins = 12472,
+ chainReaction = 195418,
+ zannesuJourney = 226852,
+ timeWarp = 80353,
+ iceFloes = 108839,
+ iceForm = 198144,
+ iceBarrier = 11426,
+ icicles = 205473,
+ frozenMass = 242253,
+ },
+ debuffs = {
+ chainsOfIce = 65173,
+ chilled = 205708,
+ frostBomb = 112948,
+ frostNova = 122,
+ iceNova = 157997,
+ wintersChill = 228358,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ --articGale = 205038,
+ boneChilling = 205027,
+ cometStorm = 153595,
+ chainReaction = 278309,
+ ebonbolt = 257537,
+ --frostBomb = 112948,
+ frozenTouch = 205030,
+ frigidWinds = 235224,
+ freezingRain = 270233,
+ focusMagic = 321358,
+ glacialSpike = 199786,
+ glacialInsulation = 235297,
+ iceNova = 157997,
+ iceFloes = 108839,
+ lonelyWinter = 205024,
+ rayOfFrost = 205021,
+ splittingIce = 56377,
+ thermalVoid = 155149,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ alterTime = 108978,
+ arcaneBrilliance = 1459,
+ arcaneExplosion = 1449,
+ blink = 1953,
+ blizzard = 10,
+ coneOfCold = 120,
+ conjureManaGem = 759,
+ conjureRefreshment = 42955,
+ conjureRefreshmentTable = 43987,
+ counterspell = 2139,
+ deepFreeze = 44572,
+ evocation = 12051,
+ fireBlast = 2136,
+ flamestrike = 2120,
+ frostArmor = 7302,
+ frostfireBolt = 44614,
+ frostNova = 122,
+ iceBlock = 45438,
+ iceLance = 30455,
+ invisibility = 66,
+ manaBomb = 125430,
+ mageArmor = 6117,
+ mirrorImage = 55342,
+ moltenArmor = 30482,
+ polymorph = 118,
+ removeCurse = 475,
+ slowFall = 130,
+ spellsteal = 30449,
+ timeWarp = 80353,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ -- arcaneIntellect = 1459,
+ -- disciplinaryCommand = 327365,
+ -- expandedPotential = 327489,
+ -- focusMagic = 321358,
+ -- freezingWinds = 327364,
+ -- iceBlock = 45438,
+ -- incantersFlow = 1463,
+ -- slowFall = 130,
+ -- slickIce = 327509,
+ -- runeOfPower = 116014,
+ },
+ conduits = {
+
+ },
+ covenants = {
+ -- deathborne = 324220,
+ -- mirrorsOfTorment = 314793,
+ -- radiantSpark = 307443,
+ -- shiftingPower = 314791,
+ },
+ debuffs = {
+ -- frostNova = 122,
+ -- mirrorsOfTorment = 314793,
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+ -- disciplinaryCommand = 327365,
+ -- sunKingsBlessings = 333313,
+ -- grislyIcicle = 333393,
+ -- firestorm = 333097,
+
+ },
+ talents = {
+ -- incantersFlow = 1463,
+ -- iceWard = 205036,
+ -- focusMagic = 321358,
+ -- ringOfFrost = 113724,
+ -- runeOfPower = 116011,
+ -- shimmer = 212653,
+ --unstableMagic = 157976,
+ },
+ },
+ },
+ MONK = {
+ -- Brewmaster
+ [268] = {
+ abilities = {
+ -- blackoutKick = 205523,
+ -- breathOfFire = 115181,
+ -- celestialBrew = 322507,
+ -- clash = 324312,
+ -- invokeNiuzao = 132578,
+ -- kegSmash = 121253,
+ -- purifyingBrew = 119582,
+ -- spinningCraneKick = 322729,
+ -- zenMeditation = 115176
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ -- eyeOfTheTiger = 196608,
+ -- ironskinBrew = 215479,
+ -- blackoutCombo = 228563,
+ -- purifiedChi = 325092,
+ -- rushingJadeWind = 116847,
+ -- zenMeditation = 115176,
+ -- celestialBrew = 322507,
+ -- invokeNiuzao = 132578,
+ -- counterStrike = 383800,
+ -- hitScheme = 383696,
+ -- shuffle = 322120,
+ -- invokeNiuzaoTheBlackOx = 132578,
+ -- weaponsOfOrder = 387184,
+ -- charredPassions = 386965,
+ -- bonedustBrew = 386276,
+ },
+ debuffs = {
+ -- breathOfFire = 146222,
+ -- moderateStagger = 124274,
+ -- heavyStagger = 124273,
+ -- kegSmash = 121253,
+ -- weaponsOfOrder = 387184,
+ -- bonedustBrew = 386276,
+ },
+ glyphs = {
+ },
+ talents = {
+ },
+ },
+ -- Mistweaver
+ [270] = {
+ abilities = {
+ -- envelopingMist = 124682,
+ -- essenceFont = 191837,
+ -- invokeYulon = 322118,
+ -- reawaken = 212051,
+ -- renewingMist = 115151,
+ -- revival = 115310,
+ -- risingSunKick = 107428,
+ -- soothingMist = 115175,
+ -- thunderFocusTea = 116680,
+ -- invokeYulonTheJadeSerpent = 322118,
+ -- invokeChiJiTheRedCrane = 325197,
+ -- fortifyingBrew = 243435,
+ -- zenFocusTea = 209584,
+ -- transcendenceTransfer = 119996,
+ -- sheilunsGift = 399491,
+ -- detox = 115450,
+ -- manaTea = 115294,
+ },
+ artifacts = {
+ },
+ buffs = {
+ -- weaponsOfOrder = 310454,
+ -- fortifyingBrew = 243435,
+ -- soothingMist = 115175,
+ -- renewingMist = 115151,
+ -- envelopingMist = 124682,
+ -- thunderFocusTea = 116680,
+ -- lifeCyclesEnvelopingMist = 197919,
+ -- lifeCyclesVivify = 197916,
+ -- surgeOfMist = 246328,
+ -- danceOfMist = 247891,
+ -- upliftTrance = 197206,
+ -- refreshingJadeWind = 196725,
+ -- lifeCocoon = 116849,
+ -- transcendence = 101643,
+ -- tigersLust = 116841,
+ -- teachingsOfTheMonastery = 202090,
+ -- diffuseMagic = 122783,
+ -- dempenHarm = 122278,
+ -- innervate = 29166,
+ -- symbolOfHope = 64901,
+ -- wayOfTheCrane = 216113,
+ -- essenceFont = 191840,
+ -- risingMist = 22170,
+ -- soothingMistJadeStatue = 198533,
+ -- envelopingBreath = 325209,
+ -- invokeChiJiTheRedCrane = 343820,
+ -- ancientTeachingOfTheMonastery = 347553,
+ -- fallenOrder = 326860,
+ -- vivaciousVivification = 392883,
+ -- rushingJadeWind = 116847,
+ -- ancientTeachings = 388023,
+ -- sheilunsGift = 399491,
+ -- manaTea = 115867,
+ },
+ debuffs = {
+ -- mysticTouch = 113746,
+ },
+ glyphs = {
+ },
+ talents = {
+ },
+ runeforges = {
+ -- ancientTeachingsOfTheMonastery = 337172,
+ -- tearOfMorning = 337473,
+ }
+ },
+ -- Windwalker
+ [269] = {
+ abilities = {
+ energizingBrew = 115288,
+ fistsOfFury = 113656,
+ flyingSerpentKick = 101545,
+ flyingSerpentKickEnd = 115057,
+ legacyOfTheWhiteTiger = 116781,
+ risingSunKick = 107428,
+ spinningFireBlossom = 115073,
+ stormEarthAndFire = 137639,
+ stormEarthAndFireFixate = 138130,
+ tigereyeBrew = 116740,--1247275,
+ touchOfKarma = 122470,
+ },
+ artifacts = {
+ },
+ buffs = {
+ comboBreakerBoK = 116768,
+ comboBreakerTP = 118864,
+ energizingBrew = 115288,
+ sparring = 116023,
+ stormEarthAndFire = 137639,
+ tigereyeBrew = 1247279,
+ tigereyeBrewUse = 1247275,
+ -- alphaTiger = 287504,
+ -- fortifyingBrew = 243435,
+ -- invokersDelight = 338321,
+ -- blackoutKick = 116768,
+ -- chiEnergy = 337571,
+ -- danceOfChiJi = 325202,
+ -- hitCombo = 196741,
+ -- pressurePoint = 247255,
+ -- rushingJadeWind = 116847,
+ -- serenity = 152173,
+ -- stormEarthAndFire = 137639,
+ -- swiftRoundhouse = 278710,
+ -- theEmperorsCapacitor = 235054,
+ -- touchOfKarma = 122470,
+ -- transferThePower = 195321,
+ -- whirlingDragonPunch = 152175,
+ -- fallenOrder = 326860,
+ -- bonedustBrew = 325216,
+ -- teachingsOfTheMonastery = 202090,
+ },
+ debuffs = {
+ risingSunKick = 130320,
+ stormEarthAndFireFixate = 138130,
+ -- disable = 116095,
+ -- disableRoot = 116706,
+ -- markOfTheCrane = 228287,
+ -- mortalWounds = 115804,
+ -- risingFist = 242259,
+ -- skyreachExhaustion = 337341,
+ -- jadefireBrand = 395414,
+ },
+ glyphs = {
+ -- glyphOfRisingTigerKick = 125151,
+ },
+ talents = {
+ },
+ traits = {
+ -- gloryOfTheDawn = 288634,
+ -- openPalmStrikes = 279918,
+ -- swiftRoundhouse = 277669,
+ },
+ runeforges = {
+ -- fatalTouch = 337296
+ }
+ },
+ -- All
+ Shared = {
+ abilities = {
+ blackoutKick = 100784,
+ cracklingJadeLightning = 117952,
+ detox = 115450,
+ disable = 116095,
+ expelHarm = 115072,
+ fortifyingBrew = 115203,
+ grappleWeapon = 117368,
+ healingSphere = 115460,
+ jab = 100780,
+ legacyOfTheEmperor = 115921,
+ -- legSweep = 119381,
+ nimbleBrew = 137562,
+ paralysis = 115078,
+ provoke = 115546,
+ reawaken = 1245934,
+ resuscitate = 115178,
+ roll = 109132,
+ spearHandStrike = 116705,
+ spinningCraneKick = 101546,
+ stanceOfTheFierceTiger = 103985,
+ tigerPalm = 100787,
+ touchOfDeath = 115080,
+ transcendence = 101643,
+ transcendenceTransfer = 119996,
+ -- touchOfFatality = 169340,
+ -- vivify = 116670,
+ -- zenFlight = 125883,
+ zenMeditation = 115176,
+ zenPilgrimage = 126892,
+ zenPilgrimageReturn = 126895,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ dampenHarm = 122278,
+ deathNote = 121125,
+ diffuseMagic = 122783,
+ fortifyingBrew = 126456,
+ legacyOfTheEmperor = 117666,
+ legacyOfTheWhiteTiger = 116781,
+ spinningCraneKick = 101546,
+ tigerPower = 125359,
+ -- theEmperorsCapacitor = 235054,
+ -- transcendence = 101643,
+ -- weaponsOfOrder = 328908, --310454,
+ -- weaponsOfOrderWW = 310454, --311054,
+ -- prideful = 340880,
+ -- vivaciousVivification = 392883,
+ -- powerStrikes = 129914, --bonus chi proc buff
+ -- hitCombo = 196741,
+ -- kicksOfFlowingMomentum = 394944,
+ -- blackoutReinforcement = 424454,
+ arcaneBrilliance = 1459, -- Mage Shared Buff
+ blessingOfKings = 20217, -- Paladin Shared Buff
+ dalaranBrilliance = 61316, -- Mage Shared Buff
+ markOfTheWild = 117666, -- Druid Shared Buff
+ leaderOfThePack = 24932, -- Druid Shared Buff
+ },
+ conduits = {
+ -- calculatedStrikes = 336526,
+ -- coordinatedOffensive = 336598,
+ -- pustuleEruption = 351094
+ },
+ covenants = {
+ -- bonedustBrew = 325216,
+ -- faelineStomp = 327104,
+ -- fallenOrder = 326860,
+ -- weaponsOfOrder = 310454,
+ },
+ debuffs = {
+ grappleWeapon = 117368,
+ zenSphere = 124098,
+ -- bonedustBrew = 325216,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ ascencion = 115396,
+ celerity = 115173,
+ chargingOxWave = 119392,
+ chiBrew = 115399,
+ chiBurst = 123986,
+ chiTorpedo = 115008,
+ chiWave = 115098,
+ dampenHarm = 122278,
+ diffuseMagic = 122783,
+ healingElixirs = 122280,
+ invokeXuenTheWhiteTiger = 123904,
+ legSweep = 119381,
+ momentum = 115174,
+ powerStrikes = 121817,
+ ringOfPeace = 116844,
+ rushingJadeWind = 116847,
+ tigersLust = 116841,
+ zenSphere = 124081,
+ },
+ },
+ },
+ PALADIN = {
+ -- Holy
+ [65] = {
+ abilities = {
+ absolution = 212056,
+ auraMastery = 31821,
+ barrierOfFaith = 148039,
+ beaconOfFaith = 156910,
+ beaconOfLight = 53563,
+ beaconOfVirtue = 200025,
+ blessingOfAutumn = 388010,
+ blessingOfSpring = 388013,
+ blessingOfSummer = 388007,
+ blessingOfWinter = 388011,
+ cleanse = 4987,
+ divineProtection = 498,
+ holyLight = 82326,
+ holyShock = 20473,
+ lightOfDawn = 85222,
+ lightOfTheMartyr = 183998,
+ --lightsHammer = 114158,
+
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ --sealOfClarity = 384810,
+ avengingCrusader = 216331,
+ auraMastery = 31821,
+ avengingWrath = 31884,
+ avengingWrathCrit = 294027,
+ afterimage = 400745,
+ beaconOfLight = 53563,
+ beaconOfFaith = 156910,
+ blessingOfSacrifice = 6940,
+ beaconOfVirtue = 200025,
+ --bestowFaith = 223306,
+ --glimmerOfLight = 287280,
+ blessingOfDawn = 337747,
+ blessingOfDusk = 385126,
+ divineProtection = 498,
+ divinePurpose = 223819,
+ --ferventMartyr = 223316,
+ infusionOfLight = 54149,
+ innervate = 29166,
+ --ruleOfLaw = 214202,
+ liberation = 461471,
+ --theLightSaves = 200423,
+ vindicator = 200376,
+ symbolOfHope = 64901,
+ divineSteed = 254474,
+ --maraadsBreath = 340459,
+ unendingLight = 394709,
+ untemperedDedication = 339990,
+ veneration = 392939,
+ },
+ debuffs = {
+ judgement = 214222,
+ judgmentOfLight = 196941,
+ --glimmerOfLight = 287280,
+ consecration = 204242,
+ dawnWillCome = 364851,
+
+ },
+ glyphs = {
+
+ },
+ talents = {
+ avengingCrusader = 216331,
+ --awakening = 248033,
+ beaconOfFaith = 156910,
+ beaconOfVirtue = 200025,
+ --bestowFaith = 223306,
+ blessingOfSummer = 388007,
+ breakingDawn = 387879,
+ crusadersMight = 196926,
+ --glimmerOfLight = 325966,
+ holyPrism = 114165,
+ judgmentOfLight = 183778,
+ --lightsHammer = 114158,
+ --ruleOfLaw = 214202,
+ --sanctifiedWrath = 53376,
+ savedByTheLight = 157047,
+ divineToll = 375576,
+ },
+ traits = {
+ graceoftheJusticar = 278593,
+ indomitableJustice = 275496,
+ },
+ runeforges = {
+ shadowbreaker = 337812,
+ shockBarrier = 337825,
+ maraadsDyingBreath = 340458,
+ }
+ },
+ -- Protection
+ [66] = {
+ abilities = {
+ ardentDefender = 31850,
+ avengersShield = 31935,
+ blessedHammer = 229976,
+ cleanseToxins = 213644,
+ divineProtection = 498,
+ guardianOfAncientKings = 86659,
+ hammerOfTheRighteous = 53595,
+ judgment = 275779,
+ judgmentsOfTheWise = 105424,
+ rebuke = 96231,
+ righteousFury = 25780,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ ardentDefender = 31850,
+ avengingWrath = 31884,
+ blessingOfSpellwarding = 204018,
+ bulwarkOfOrder = 209388,
+ consecration = 188370,
+ divineShield = 642,
+ divinePurpose = 223819,
+ guardianOfAncientKings = 86659,
+ shieldOfTheRighteous = 132403,
+ shiningLight = 327510,
+ avengersValor = 197561,
+ divineSteed = 254474,
+ royalDecree = 340147,
+ },
+ debuffs = {
+ blessedHammer = 204301,
+ judgmentOfLight = 196941,
+ judgment = 197277,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ layOnHands = 633,
+ blessedHammer = 204019,
+ hammerOfTheRighteous = 53595,
+ innerLight = 386568,
+ redoubt = 280373,
+ holyShield = 152261,
+ grandCrusader = 85043,
+ -- shiningLight = 321136, -- No longer a talent?
+ consecratedGround = 204054,
+ improvedLayOnHands = 393027,
+ inspiringVanguard = 393022,
+ ardentDefender = 31850,
+ barricadeOfFaith = 385726,
+ consecrationInFlame = 379022,
+ improvedHolyShield = 393030,
+ sanctuary = 379021,
+ bulwarkOfOrder = 209389,
+ improvedArdentDefender = 393114,
+ blessingOfSpellwarding = 204018,
+ lightOfTheTitans = 378405,
+ strengthInAdversity = 393071,
+ crusadersResolve = 380188,
+ tyrsEnforcer = 378285,
+ relentlessInquisitor = 383388,
+ aveningWrathMight = 31884,
+ sentinel = 389539,
+ handOTheProtector = 315924,
+ strengthOfConviction = 379008,
+ resoluteDefender = 385422,
+ bastionOfLight = 378974,
+ guardianOfAncientKings = 86659,
+ crusadersJudgement = 204023,
+ uthersCounsel = 378425,
+ focusedEnmity = 378845,
+ soaringShield = 378457,
+ giftOfTheGoldenValkyr = 378279,
+ eyeOfTyr = 387174,
+ righteousProtector = 204074,
+ faithInTheLight = 379043,
+ ferrenMarcussFervor = 378762,
+ faithsArmor = 379017,
+ finalStand = 204077,
+ divineToll = 375576,
+ momentOfGlory = 327193,
+ bulwarkOfTheRighteousFury = 386653,
+ divineResonance = 386738,
+ quickenedInvocations = 379391,
+ },
+ traits = {
+ bulwarkOfLight = 272976,
+ },
+ },
+ -- Retribution
+ [70] = {
+ abilities = {
+ -- divineProtection = 403876,
+ divineStorm = 53385,
+ emancipation = 121783,
+ exorcism = 879,
+ guardianOfAncientKings = 86659,
+ hammerOfTheRighteous = 53595,
+ inquisition = 84963,
+ judgmentsOfTheBold = 111529,
+ sealOfJustice = 20164,
+ templarsVerdict = 85256,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ ancientPower = 86700,
+ -- crusade = 231895,
+ -- divineArbiter = 406975,
+ divineCrusader = 144595,
+ -- divinePurpose = 223819,
+ -- divineRight = 278523,
+ -- empyreanLegacy = 387178,
+ -- empyreanPower = 326733, --286393,
+ inquisition = 84963,
+ -- finalVerdict = 414949,
+ -- selflessHealer = 114250,
+ -- templarStrikes = 406648,
+ },
+ debuffs = {
+ -- executionSentence = 267798,
+ -- expurgation = 383346,
+ -- finalReckoning = 343721,
+ },
+ glyphs = {
+ -- glyphOfWingedVengeance = 57979,
+ },
+ talents = {
+ },
+ traits = {
+ -- divineRight = 278523,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ absolution = 450761,
+ avengingWrath = 31884,
+ blessingOfKings = 20217,
+ blessingOfMight = 19740,
+ blindingLight = 115750,
+ cleanse = 4987,
+ crusaderStrike = 35395,
+ devotionAura = 31821,
+ divineProtection = 498,
+ divineShield = 642,
+ flashOfLight = 19750,
+ hammerOfJustice = 853,
+ hammerOfWrath = 24275,
+ handOfFreedom = 1044,
+ handOfProtection = 1022,
+ handOfSacrifice = 6940,
+ handOfSalvation = 1038,
+ judgment = 20271,
+ layOnHands = 633,
+ rebuke = 96231,
+ reckoning = 62124,
+ redemption = 7328,
+ righteousFury = 25780,
+ sealOfCommand = 105361,
+ sealOfInsight = 20165,
+ sealOfRighteousness = 20154,
+ sealOfTruth = 31801,
+ turnEvil = 10326,
+ wordOfGlory = 85673,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ avengingWrath = 31884,
+ -- blessingOfProtection = 1022,
+ -- consecration = 26573,
+ -- concentrationAura = 317920,
+ -- crusaderAura = 32223,
+ -- devotionAura = 465,
+ divinePurpose = 90174,
+ -- divineResonance = 384029,
+ -- divineShield = 642,
+ -- divineSteed = 221883,
+ -- echoesOfWrath = 423590,
+ holyAvenger = 105809,
+ -- retributionAura = 183435,
+ -- seraphim = 152262,
+ sealOfCommand = 105361,
+ -- vanquishersHammer = 328204,
+ },
+ conduits = {
+ -- expurgation = 339371,
+ -- templarsVindication = 339531,
+ },
+ covenants = {
+ --ashenHallow = 316958,
+ --blessingOfTheSeasons = 328278,
+ --blessingOfAutumn = 388010,
+ --blessingOfSpring = 388013,
+ --blessingOfSummer = 388007,
+ --blessingOfWinter = 388011,
+ --vanquishersHammer = 328204,
+ },
+ debuffs = {
+ -- blindingLight = 105421,
+ forbearance = 25771,
+ -- hammerOfJustice = 853,
+ -- judgment = 197277,
+ -- turnEvil = 10326,
+ -- handOfReckoning = 62124,
+ },
+ glyphs = {
+ -- glyphOfFireFromHeavens = 57954,
+ -- glyphOfPillarOfLight = 146959,
+ -- glyphOfTheLuminousCharger = 89401,
+ -- glyphOfTheQueen = 212642,
+ },
+ talents = {
+ clemency = 105622,
+ divinePurpose = 86172,
+ eternalFlame = 114163,
+ evilIsAPointOfView = 110301,
+ executionSentence = 114157,
+ fistOfJustice = 105593,
+ handOfPurity = 114039,
+ holyAvenger = 105809,
+ holyPrism = 114165,
+ lightsHammer = 114158,
+ longArmOfTheLaw = 87172,
+ pursuitOfJustice = 26023,
+ repentance = 20066,
+ sacredShield = 20925,
+ sanctifiedWrath = 53376,
+ selflessHealer = 85804,
+ speedOfLight = 85499,
+ unbreakableSpirit = 114154,
+ },
+ runeforges = {
+ -- finalVerdict = 337247,
+ -- theMadParagon = 337594,
+ -- theMagistratesJudgment = 337681,
+ -- vanguardsMomentum = 337638,
+ }
+ },
+ },
+ PRIEST = {
+ -- Discipline
+ [256] = {
+ abilities = {
+ angelicFeather = 121536,
+ --clarityOfWill = 152118,
+ divineStar = 110744,
+ desperatePrayer = 19236,
+ evangelism = 246287,
+ flashHeal = 2061,
+ halo = 120517,
+ holyNova = 132157,
+ leapOfFaith = 73325,
+ mindbender = 123040,
+ mindBlast = 8092,
+ mindControl = 205364,
+ mindSear = 48045,
+ mindSoothe = 453,
+ mindVision = 2096,
+ painSuppression = 33206,
+ penance = 47540,
+ --plea = 200829,
+ powerInfusion = 10060,
+ powerWordBarrier = 62618,
+ powerWordRadiance = 194509,
+ powerWordShield = 17,
+ powerWordSolace = 129250,
+ psychicScream = 8122,
+ purgeTheWicked = 204197,
+ purify = 527,
+ rapture = 47536,
+ schism = 214621,
+ shadowfiend = 34433,
+ shadowMend = 186263,
+ shiningForce = 204263,
+ sinsOfTheMany = 198076,
+ spiritShell = 109964,
+ smite = 585,
+ },
+ artifacts = {
+ --sinsOfTheMany = 198074,
+ },
+ buffs = {
+ angelicFeather = 121557,
+ atonement = 194384,
+ bodyAndSoul = 65081,
+ borrowedTime = 197763,
+ depthOfTheShadows = 275544,
+ --clarityOfWill = 152118,
+ innervate = 29166,
+ overloadedWithLight = 223166,
+ penitent = 246519,
+ --powerInfusion = 10060,
+ powerOfTheDarkSide = 198069,
+ powerWordShield = 17,
+ rapture = 47536,
+ speedOfThePious = 197767,
+ spiritShell = 109964,
+ symbolOfHope = 64901,
+ },
+ debuffs = {
+ weakenedSoul = 6788,
+ purgeTheWicked = 204213,
+ schism = 214621,
+ smite = 585,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ angelicFeather = 121536,
+ bodyAndSoul = 64129,
+ castigation = 193134,
+ --clarityOfWill = 152118,
+ contrition = 197419,
+ divineStar = 110744,
+ dominantMind = 205367,
+ evangelism = 246287,
+ --grace = 200309,
+ halo = 120517,
+ lenience = 238063,
+ masochism = 193063,
+ mindbender = 123040,
+ --powerInfusion = 10060,
+ powerWordSolace = 129250,
+ purgeTheWicked = 204197,
+ psychicVoice = 196704,
+ schism = 214621,
+ shadowCovenant = 314867,
+ shieldDiscipline = 197045,
+ spiritShell = 109964,
+ sinsOfTheMany = 280391,
+ shiningForce = 204263,
+ --thePenitent = 200347,
+ twistOfFate = 265259,
+ },
+ traits = {
+ giftOfForgiveness = 277680,
+ },
+ },
+ -- Holy
+ [257] = {
+ abilities = {
+ angelicFeather = 121536,
+ bodyAndMind = 214121,
+ circleOfHealing = 204883,
+ desperatePrayer = 19236,
+ divineHymn = 64843,
+ divineStar = 110744,
+ flashHeal = 2061,
+ guardianSpirit = 47788,
+ holyFire = 14914,
+ holyNova = 132157,
+ holyWordChastise = 88625,
+ holyWordSanctify = 34861,
+ holyWordSerenity = 2050,
+ holyWordSalvation = 265202,
+ heal = 2060,
+ leapOfFaith = 73325,
+ lightOfTuure = 33076,
+ prayerOfHealing = 596,
+ prayerOfMending = 33076,
+ purify = 527,
+ renew = 139,
+ smite = 585,
+ symbolOfHope = 64901,
+ },
+ artifacts = {
+ -- lightOfTuure = 208065,
+ },
+ buffs = {
+ angelicFeather = 121557,
+ blessingOfTuure = 196578,
+ divinity = 197031,
+ echoOfLight = 77489,
+ flashConcentration = 336267,
+ powerOfTheNaaru = 196489,
+ prayerOfMending = 41635,
+ renew = 139,
+ surgeOfLight = 114255,
+ spiritOfRedemption = 27827,
+ },
+ debuffs = {
+
+
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+ flashConcentration = 336266,
+ },
+ talents = {
+ angelicFeather = 121536,
+ apotheosis = 200183,
+ bodyAndSoul = 64129,
+ --bodyAndMind = 214121,
+ divineStar = 110744,
+ --divinity = 197031,
+ halo = 120517,
+ --piety = 197034,
+ shiningForce = 204263,
+ surgeOfLight = 109186,
+ --symbolOfHope = 64901,
+ },
+ },
+ -- Shadow
+ [258] = {
+ abilities = {
+ damnation = 341374,
+ darkAscension = 280711,
+ dispersion = 47585,
+ devouringPlague = 335467,
+ mindBlast = 8092,
+ mindBomb = 205369,
+ mindbender = 200174,
+ mindFlay = 15407,
+ mindSear = 48045,
+ mindVision = 2096,
+ powerInfusion = 10060,
+ powerWordShield = 17,
+ psychicHorror = 64044,
+ psychicScream = 8122,
+ purifyDisease = 213634,
+ shadowMend = 186263,
+ shadowWordVoid = 205351,
+ shadowfiend = 34433,
+ shadowform = 232698,
+ silence = 15487,
+ surrenderToMadness = 193223,
+ vampiricEmbrace = 15286,
+ vampiricTouch = 34914,
+ voidBolt = 205448,
+ devoidBolt = 343355,
+ voidEruption = 228260,
+ voidForm = 228264,
+ },
+ artifacts = {
+ lashOfInsanity = 238137,
+ massHysteria = 194378,
+ sphereOfInsanity = 194179,
+ toThePain = 193644,
+ touchOfDarkness = 194007,
+ unleashTheShadows = 194093,
+ voidCorruption = 194016,
+ voidTorrent = 205065,
+ },
+ buffs = {
+ chorusOfInsanity = 279572,
+ darkThoughts = 341207,
+ dispersion = 47585,
+ dissonantEchoes = 343144,
+ harvestedThoughts = 288343,
+ lingeringInsanity = 199849,
+ powerWordShield = 17,
+ powerInfusion = 10060,
+ shadowyInsight = 124430,
+ shadowform = 232698,
+ surrenderedSoul = 212570,
+ surrenderToMadness = 193223,
+ thoughtsHarvester = 288340,
+ unfurlingDarkness = 341282,
+ void = 211657,
+ voidForm = 194249,
+ voidTorrent = 205065,
+ zeksExterminatus = 236546, -- Legendary Cloak proc
+ },
+ debuffs = {
+ devouringPlague = 335467,
+ hungeringVoid = 345218,
+ mindFlay = 15407,
+ vampiricTouch = 34914,
+ weakenedSoul = 6788,
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+ shadowflamePrism = 336143,
+ },
+ talents = {
+ fortressOfTheMind = 193195,
+ deathAndMadness = 321291,
+ unfurlingDarkness = 341273,
+ bodyAndSoul = 64129,
+ sanlayn = 199855,
+ twistOfFate = 109142,
+ misery = 238558,
+ searingNightmare = 341385,
+ mindBomb = 205369,
+ psychicHorror = 64044,
+ auspiciousSpirits = 155271,
+ psychicLink = 199484,
+ shadowCrash = 205385,
+ damnation = 341374,
+ mindbender = 200174,
+ voidTorrent = 263165,
+ ancientMadness = 341240,
+ hungeringVoid = 345218,
+ surrenderToMadness = 319952,
+ },
+ traits = {
+ chorusOfInsanity = 278661,
+ deathThroes = 278659,
+ searingDialogue = 272788,
+ spitefulApparitions = 277682,
+ thoughtHarvester = 288340,
+ whispersOfTheDamned = 275722,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ ascendedBlast = 325283,
+ ascendedNova = 325020,
+ boonOfTheAscended = 325013,
+ desperatePrayer = 19236,
+ dispelMagic = 528,
+ fade = 586,
+ faeGuardians = 327661,
+ flashHeal = 2061,
+ leapOfFaith = 73325,
+ levitate = 1706,
+ massDispel = 32375,
+ massResurrection = 212036,
+ mindBlast = 8092,
+ mindControl = 605,
+ mindgames = 323673,
+ mindSoothe = 453,
+ mindVision = 2096,
+ powerInfusion = 10060,
+ powerWordFortitude = 21562,
+ powerWordShield = 17,
+ psychicScream = 8122,
+ resurrection = 2006,
+ shadowWordDeath = 32379,
+ shackleUndead = 9484,
+ shadowWordPain = 589,
+ smite = 585,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ classHallSpeed = 224098,
+ boonOfTheAscended = 325013,
+ faeGuardians = 327661,
+ levitate = 111759,
+ powerWordFortitude = 21562,
+ powerWordShield = 17,
+
+ },
+ conduits = {
+ dissonantEchoes = 338342,
+ },
+ covenants = {
+ boonOfTheAscended = 325013,
+ ascendedBlast = 325315,
+ ascendedEruption = 325326,
+ ascendedNova = 325020,
+ faeGuardians = 327661,
+ mindgames = 323673,
+ unholyNova = 324724,
+ },
+ debuffs = {
+ shadowWordPain = 589,
+ weakenedSoul = 6788,
+ wrathfulFaerie = 327703,
+
+ },
+ glyphs = {
+
+ },
+ talents = {
+
+ },
+ },
+ },
+ ROGUE = {
+ -- Assassination
+ [259] = {
+ abilities = {
+ -- ambush = 8676,
+ -- crimsonTempest = 121411,
+ -- deadlyPoison = 315584,
+ -- envenom = 32645,
+ -- eviscerate = 196819,
+ -- exsanguinate = 200806,
+ -- fanOfKnives = 51723,
+ -- garrote = 703,
+ -- mutilate = 1329,
+ -- poisonedKnife = 185565,
+ -- rupture = 1943,
+ -- shadowstep = 36554,
+ -- sinisterStrike = 1752,
+ -- vendetta = 79140,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ blindside = 121153,
+ deadlyPoison = 2823,
+ elaboratePlanning = 193641,
+ envenom = 32645,
+ leechingPoison = 108211,
+ sharpenedBlades = 272916,
+ masterAssassin = 256735,
+ subterfuge = 115192,
+ theDreadlordsDeceit = 208692,
+ },
+ debuffs = {
+ crimsonTempest = 121411,
+ deadlyPoison = 2818,
+ garrote = 703,
+ internalBleeding = 154953,
+ rupture = 1943,
+ surgeOfToxins = 192425,
+ toxicBlade = 245389,
+ vendetta = 79140,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ blindside = 328085,
+ crimsonTempest = 121411,
+ elaboratePlanning = 193640,
+ elusiveness = 79008,
+ exsanguinate = 200806,
+ hiddenBlades = 270061,
+ internalBleeding = 154904,
+ ironWire = 196861,
+ leechingPoison = 280716,
+ masterAssassin = 255989,
+ masterPoisoner = 196864,
+ nightstalker = 14062,
+ poisonBomb = 255544,
+ subterfuge = 108208,
+ venomRush = 152152,
+ },
+ traits = {
+ doubleDose = 273007,
+ echoingBlades = 287649,
+ sharpenedBlades = 272911,
+ shroudedSuffocation = 278666,
+ twistTheKnife = 273488,
+ },
+ runeforges = {
+ dashingScoundrel = 340081,
+ doomblade = 340082,
+ zoldyckInsignia = 340083,
+ duskwalkersPatch = 340084,
+ },
+ },
+ -- Outlaw
+ [260] = {
+ abilities = {
+ adrenalineRush = 13750,
+ ambush = 8676,
+ betweenTheEyes = 315341,
+ bladeFlurry = 13877,
+ bladeRush = 271877,
+ blind = 2094,
+ curseOfTheDreadblades = 202665,
+ dispatch = 2098,
+ ghostlyStrike = 196937,
+ gouge = 1776,
+ grapplingHook = 195457,
+ killingSpree = 51690,
+ masteryMainGauche = 76806,
+ pistolShot = 185763,
+ riposte = 199754,
+ rollTheBones = 315508,
+ sinisterStrike = 193315,
+ dreadblades = 343142,
+ },
+ artifacts = {
+ blackPowder = 216230,
+ bladeDancer = 202507,
+ bladeMaster = 202628,
+ blunderbuss = 202897,
+ blurredTime = 202769,
+ cannonballBarrage = 185767,
+ curseOfTheDreadblades = 202665,
+ cursedEdge = 202463,
+ cursedSteel = 214929,
+ deception = 202755,
+ fatesThirst = 202514,
+ fatebringer = 202524,
+ fortuneStrikes = 202530,
+ fortunesBoon = 202907,
+ fortunesStrike = 202521,
+ ghostlyShell = 202533,
+ greed = 202820,
+ gunslinger = 202522,
+ hiddenBlade = 202573,
+ killingSpree = 51690,
+ loadedDice = 238139,
+ },
+ buffs = {
+ adrenalineRush = 13750,
+ alacrity = 193538,
+ bladeFlurry = 13877,
+ blunderbuss = 202895,
+ broadside = 193356,
+ buriedTreasure = 199600,
+ deadShot = 272940,
+ grandMelee = 193358,
+ greenskinsWaterloggedWristcuffs = 209420,
+ hiddenBlade = 202754,
+ jollyRoger = 199603,
+ loadedDice = 256171,
+ opportunity = 195627,
+ ruthlessPrecision = 193357,
+ rollTheBones = {
+ broadside = 193356,
+ buriedTreasure = 199600,
+ grandMelee = 193358,
+ ruthlessPrecision = 193357,
+ skullAndCrossbones = 199603,
+ trueBearing = 193359,
+ },
+ sharkInfestedWaters = 193357,
+ skullAndCrossbones = 199603,
+ snakeeeyes = 275863, --typo, leaving in not to break stuff
+ snakeeyes = 275863,
+ swordplay = 211669,
+ trueBearing = 193359,
+ wits = 288988,
+ },
+ debuffs = {
+ ghostlyStrike = 196937,
+ gouge = 1776,
+ betweenTheEyes = 199804,
+ preyOnTheWeak = 255909,
+ blind = 2094,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ acrobaticStikes = 196924, -- typo, leaving in to not break old profiles
+ acrobaticStrikes = 196924,
+ dirtyTricks = 108216,
+ bladeRush = 271877,
+ ghostlyStrike = 196937,
+ grapplingHook = 256188,
+ hitAndRun = 196922,
+ ironStomach = 193546,
+ killingSpree = 51690,
+ weaponmaster = 200733,
+ quickDraw = 196938,
+ blindingPowder = 256165,
+ loadedDice = 256170,
+ dancingSteel = 272026,
+ dreadblades = 343142,
+ },
+ traits = {
+ deadshot = 272935,
+ aceupyoursleeve = 278676,
+ snakeeyes = 275846,
+ keepYourWitsAboutYou = 288979,
+
+ },
+ runeforges = {
+ greenskinsWickers = 340085,
+ guileCharm = 340086,
+ celerity = 340087,
+ concealedBlunderbuss = 340088,
+ },
+ conduits = {
+ countTheOdds = 341546,
+ },
+ },
+ -- Subtlety (MoP Classic - 5.4.8)
+ [261] = {
+ abilities = {
+ ambush = 8676,
+ backstab = 53,
+ crimsonTempest = 121411,
+ eviscerate = 2098,
+ fanOfKnives = 51723,
+ hemorrhage = 16511,
+ preparation = 14185,
+ premeditation = 14183,
+ rupture = 1943,
+ shadowBlades = 121471,
+ shadowDance = 121471, -- MoP Classic uses same ID as Shadow Blades
+ shadowstep = 36554,
+ shurikenToss = 114014,
+ sliceAndDice = 5171,
+ vanish = 1856,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ anticipation = 115189, -- Talent buff for extra combo points
+ masterOfSubtlety = 31665, -- Shadow Dance / Stealth buff
+ shadowBlades = 121471,
+ shadowDance = 121471, -- MoP Classic uses same ID as Shadow Blades
+ shadowmeld = 58984, -- Night Elf racial
+ sliceAndDice = 5171,
+ sleightOfHand = 145211, -- Proc from Ambush
+ stealth = 1784,
+ subterfuge = 115192, -- Talent - Stealth linger
+ vanish = 1856,
+ },
+ debuffs = {
+ crimsonTempest = 122233,
+ findWeakness = 91023, -- Shadow Dance debuff
+ hemorrhage = 16511,
+ rupture = 1943,
+ },
+ glyphs = {
+ glyphOfAmbush = 56813,
+ glyphOfBackstab = 63254,
+ glyphOfHemorrhage = 56807,
+ glyphOfPreparation = 63249,
+ glyphOfShadowDance = 63253,
+ glyphOfSliceAndDice = 146638,
+ glyphOfVanish = 56814,
+ },
+ talents = {
+ -- Tier 1 (Level 15)
+ nightstalker = 14062,
+ subterfuge = 108208,
+ shadowFocus = 108209,
+ -- Tier 2 (Level 30) - Shared
+ deadlyThrow = 26679,
+ combatReadiness = 74001,
+ -- Tier 3 (Level 45)
+ cheatDeath = 31230,
+ leechingPoison = 108211,
+ elusiveness = 79008,
+ -- Tier 4 (Level 60)
+ cloakAndDagger = 138106,
+ shadowstep = 36554,
+ burstOfSpeed = 108212,
+ -- Tier 5 (Level 75)
+ preyOnTheWeak = 131511,
+ paralyticPoison = 108215,
+ dirtyTricks = 108216,
+ -- Tier 6 (Level 90)
+ shurikenToss = 114014,
+ markedForDeath = 137619,
+ anticipation = 114015,
+ },
+ traits = {
+
+ },
+ runeforges = {
+
+ },
+ conduits = {
+
+ },
+ },
+ -- All (MoP Classic - 5.4.8)
+ Shared = {
+ abilities = {
+ ambush = 8676,
+ blind = 2094,
+ cheapShot = 1833,
+ cloakOfShadows = 31224,
+ combatReadiness = 74001, -- MoP defensive CD
+ cripplingPoison = 3408,
+ deadlyPoison = 2823,
+ distract = 1725,
+ evasion = 5277,
+ eviscerate = 2098,
+ feint = 1966,
+ gouge = 1776,
+ instantPoison = 6603, -- MoP version
+ kick = 1766,
+ kidneyShot = 408,
+ mindNumbingPoison = 5761, -- MoP version (previously Crippling in later expansions)
+ paralyticPoison = 108211, -- MoP poison
+ pickLock = 1804,
+ pickPocket = 921,
+ redirect = 73981, -- MoP ability
+ recuperate = 73651, -- MoP healing finisher
+ sap = 6770,
+ shroudOfConcealment = 114018, -- MoP group stealth
+ sinisterStrike = 1752,
+ sliceAndDice = 5171,
+ sprint = 2983,
+ stealth = 1784,
+ throw = 2764, -- MoP version
+ tricksOfTheTrade = 57934,
+ vanish = 1856,
+ woundPoison = 8679,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ cloakOfShadows = 31224,
+ combatReadiness = 74001,
+ cripplingPoison = 3408,
+ deadlyPoison = 2823,
+ evasion = 5277,
+ feint = 1966,
+ instantPoison = 6603,
+ mindNumbingPoison = 5761,
+ paralyticPoison = 108211,
+ recuperate = 73651,
+ shroudOfConcealment = 114018,
+ sliceAndDice = 5171,
+ sprint = 2983,
+ stealth = 1784,
+ tricksOfTheTrade = 57934,
+ vanish = 1856,
+ woundPoison = 8679,
+ },
+ conduits = {
+
+ },
+ covenants = {
+
+ },
+ debuffs = {
+ blind = 2094,
+ cheapShot = 1833,
+ cripplingPoison = 3408,
+ garrote = 703,
+ gouge = 1776,
+ kidneyShot = 408,
+ mindNumbingPoison = 5761,
+ paralyticPoison = 113952, -- Debuff version
+ rupture = 1943,
+ sap = 6770,
+ woundPoison = 8679,
+ },
+ glyphs = {
+ glyphOfAdrenalineRush = 56803,
+ glyphOfBlind = 91299,
+ glyphOfCloakOfShadows = 63269,
+ glyphOfExposedArmor = 56805,
+ glyphOfFanOfKnives = 63254,
+ glyphOfFeint = 63257,
+ glyphOfGarrote = 56812,
+ glyphOfGouge = 56809,
+ glyphOfKick = 63252,
+ glyphOfKillingSpree = 63249,
+ glyphOfSap = 56798,
+ glyphOfSprint = 63250,
+ glyphOfTricksOfTheTrade = 63256,
+ },
+ talents = {
+ -- Tier 2 (Level 30) - Shared across all Rogue specs
+ deadlyThrow = 26679,
+ combatReadiness = 74001,
+ },
+ runeforges = {
+
+ },
+ },
+ },
+ SHAMAN = {
+ -- Elemental
+ [262] = {
+ abilities = {
+ earthquake = 61882,
+ earthShock = 8042,
+ eyeOfTheStorm = 157375,
+ fireBlast = 57984,
+ fireElemental = 198067,
+ hardenSkin = 118337,
+ immolate = 118297,
+ lavaBeam = 114074,
+ meteor = 117588,
+ pulverize = 118345,
+ windGust = 157331,
+ },
+ artifacts = {
+ },
+ conduits = {
+ callOfFlame = 338303,
+ },
+ buffs = {
+ ascendance = 114050,
+ bloodlust = 2825,
+ echoingShock = 320125,
+ elementalFocus = 16164,
+ elementalMastery = 16166,
+ emberTotem = 210658,
+ hardenSkin = 118337,
+ heroism = 32182,
+ iceFury = 210714,
+ lavaSurge = 77762,
+ masterOfTheElements = 260734,
+ powerOfTheMaelstrom = 191877,
+ resonanceTotem = 202192,
+ stormKeeper = 191634,
+ stormTotem = 210652,
+ surgeOfPower = 285514,
+ tailwindTotem = 210659,
+ tectonicThunder = 286949,
+ windGust = 263806,
+ },
+ debuffs = {
+ flameShock = 188389,
+ immolate = 118297,
+ lightningRod = 197209,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ aftershock = 273221,
+ ancestralGuidance = 108281,
+ ascendance = 114050,
+ earthenRage = 170374,
+ echoingShock = 320125,
+ echoOfTheElements = 333919,
+ elementalBlast = 117014,
+ iceFury = 210714,
+ liquidMagmaTotem = 192222,
+ masterOfTheElements = 16166,
+ primalElementalist = 117013,
+ staticDischarge = 342243,
+ stormElemental = 192249,
+ stormKeeper = 191634,
+ surgeOfPower = 262303,
+ unlimitedPower = 260895,
+
+ },
+ traits = {
+ naturalHarmony = 278697,
+ tectonicThunder = 286949,
+ },
+ },
+ -- Enhancement
+ [263] = {
+ abilities = {
+ feralSpirit = 51533,
+ fireNova = 1535,
+ lavaLash = 60103,
+ searingTotem = 3599,
+ shamanisticRage = 30823,
+ spiritWalk = 58875,
+ stormblast = 115356,
+ stormstrike = 17364,
+ windfuryWeapon = 8232,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ ascendance = 114051,
+ -- ashenCatalyst = 390371,
+ -- awakeningStorms = 462131,
+ -- cracklingSurge = 224127,
+ -- crashLightning = 187878,
+ -- clCrashLightning = 333964,
+ -- convergingStorms = 198300,
+ -- doomWinds = 384352,
+ -- earthenWeapon = 392375,
+ -- feralSpirit = 333957,
+ -- hailstorm = 334196,
+ -- hotHand = 215785,
+ -- icyEdge = 224126,
+ -- legacyOfTheFrostWitch = 384451,
+ -- maelstromOfElements = 394677,
+ maelstromWeapon = 53817,
+ -- moltenWeapon = 224125,
+ -- primordialWave = 327164,
+ -- splinteredElements = 354648,
+ -- tempest = 454015,
+ -- stormkeeper = 320137,
+ -- stormbringer = 201845,
+ },
+ debuffs = {
+ -- doomWinds = 335904,
+ -- lashingFlames = 334168,
+ },
+ glyphs = {
+ glyphOfFrostShock = 55443,
+ },
+ talents = {
+ },
+ traits = {
+ -- lightningConduit = 275389,
+ -- naturalHarmony = 278697,
+ -- primalPrimer = 272992,
+ -- strengthOfTheEarth = 273461,
+ },
+ },
+ -- Restoration
+ [264] = {
+ abilities = {
+ ancestralVision = 212048,
+ waterShield = 52127,
+ healingRain = 73920,
+ healingTideTotem = 108280,
+ healingWave = 77472,
+ manaTideTotem = 16191,
+ purifySpirit = 77130,
+ riptide = 61295,
+ spiritLinkTotem = 98008,
+ recallCloudburstTotem = 201764,
+ },
+ artifacts = {
+ -- giftOfTheQueen = 207778,
+ },
+ buffs = {
+ ascendance = 114052,
+ cloudburstTotem = 157504,
+ waterShield = 52127,
+ healingRain = 73920,
+ heavyRainfall = 338344,
+ jonatsFocus = 210607,
+ lavaSurge = 77762,
+ riptide = 61295,
+ tidalWaves = 53390,
+ unleashLife = 73685,
+ undulation = 216251,
+ swirlingCurrents = 338340,
+ },
+ conduits = {
+ heavyRainfall = 338343,
+ },
+ debuffs = {
+ flameShock = 188389,
+ },
+ glyphs = {
+ },
+ talents = {
+ ancestralVigor = 207401,
+ ancestralProtectionTotem = 207399,
+ ascendance = 114052,
+ cloudburstTotem = 157153,
+ deluge = 200076,
+ downpour = 207778,
+ earthenWallTotem = 198838,
+ echoOfTheElements = 108283,
+ flashFlood = 280614,
+ highTide = 157154,
+ sugeofEarth = 320746,
+ torrent = 200072,
+ undulation = 200071,
+ unleashLife = 73685,
+ wellspring = 197995,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ ascendance = 114049,
+ ancestralSpirit = 2008,
+ ancestralVision = 450762,
+ astralRecall = 556,
+ bindElemental = 76780,
+ bloodlust = 2825,
+ capacitorTotem = 108269,
+ chainHeal = 1064,
+ chainLightning = 421,
+ cleanseSpirit = 51886,
+ earthbindTotem = 2484,
+ earthElementalTotem = 2062,
+ earthShock = 8042,
+ farSight = 6196,
+ fireElementalTotem = 2894,
+ flameShock = 8050,
+ flametongueWeapon = 8024,
+ frostbrandWeapon = 8033,
+ frostShock = 8056,
+ ghostWolf = 2645,
+ groundingTotem = 8177,
+ healingRain = 73920,
+ healingStreamTotem = 5394,
+ healingSurge = 8004,
+ healingTideTotem = 108280,
+ heroism = 32182,
+ hex = 51514,
+ lightningBolt = 403,
+ lightningShield = 324,
+ magmaTotem = 8190,
+ primalStrike = 73899,
+ purge = 370,
+ rockbiterWeapon = 8017,
+ searingTotem = 3599,
+ spiritwalkersGrace = 79206,
+ stormlashTotem = 120668,
+ totemicRecall = 36936,
+ tremorTotem = 8143,
+ unleashElements = 73680,
+ waterShield = 52127,
+ waterWalking = 546,
+ windShear = 57994,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ ancestralSwiftness = 16188,
+ astralShift = 108271,
+ -- earthShield = 974,
+ flametongueWeapon = 8024,
+ flameShock = 8050,
+ ghostWolf = 2645,
+ lightningShield = 324,
+ -- skyfury = 462854,
+ -- spiritwalkersGrace = 79206,
+ unleashFlame = 73683,
+ waterShield = 52127,
+ waterWalking = 546,
+ },
+ conduits = {
+ -- astralProtection = 337964,
+ },
+ covenants = {
+ -- chainHarvest = 320674,
+ -- faeTransfusion = 328923,
+ -- primordialWave = 326059,
+ -- vesperTotem = 324386,
+ },
+ debuffs = {
+ flameShock = 8050,
+ -- frostShock = 196840,
+ -- hex = 51514,
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+ -- chainsOfDevastation = 336735,
+ -- doomWinds = 335902,
+ -- elementalEquilibrium = 336730,
+ -- primalLavaActuators = 335895,
+ -- echoesOfGreatSundering = 336215,
+ -- seedsOfRampantGrowth = 356218,
+ -- skybreakersFieryDemise = 336734,
+ -- spiritwalkersTidalTotem = 335891,
+ -- deeptremorStone = 336739,
+ -- windspeakersLavaResurgence = 336063,
+ -- deeplyRootedElements = 336738,
+ },
+ talents = {
+ ancestralGuidance = 108281,
+ ancestralSwiftness = 16188,
+ astralShift = 108271,
+ callOfTheElements = 108285,
+ conductivity = 108282,
+ earthgrabTotem = 51485,
+ echoOfTheElements = 108283,
+ elementalBlast = 117014,
+ elementalMastery = 16166,
+ frozenPower = 63374,
+ naturesGuardian = 30884,
+ primalElementalist = 117013,
+ rushingStreams = 147074,
+ stoneBulwarkTotem = 108270,
+ totemicPersistence = 108284,
+ totemicProjection = 108287,
+ unleashedFury = 117012,
+ windwalkTotem = 108273,
+ },
+ talentsHeroic = {
+ -- -- Enhance / Elemental Stormbringer
+ -- unlimitedPower = 454391,
+ -- supercharge = 455110,
+ -- voltaicSurge = 454919,
+ -- awakeningStorms = 455129,
+ -- stormcaller = 454021,
+ -- stormSwell = 455088,
+ -- arcDischarge = 455096,
+ -- conductiveEnergy = 455123,
+ -- shockingGrasp = 454022,
+ -- rollingThunder = 454026,
+ -- naturesProtection = 454027,
+ -- surgingCurrents = 454372,
+ -- -- Enhance / Restoration Totemic
+ -- surgingTotem = 444995,
+ -- totemicRebound = 445025,
+ -- oversizedTotems = 445026,
+ -- swiftRecall = 445027,
+ -- imbuementMastery = 445028,
+ -- whirlingElements = 445024,
+ -- amplificationCore = 445029,
+ -- oversurge = 445030,
+ -- windBarrier = 445031,
+ -- pulseCapacitor = 445032,
+ -- supportiveImbuements = 445033,
+ -- livelyTotems = 445034,
+ -- reactivity = 445035,
+ -- totemicCoordination = 445036,
+ -- earthsurge = 455590,
+ -- -- Elemental / Restoration Farseer
+ -- callOfTheAncestors = 443450,
+ -- latentWisdom = 443449,
+ -- ancientFellowship = 443423,
+ -- offeringFromBeyond = 443451,
+ -- naturalHarmony = 443442,
+ -- earthenCommunion = 443441,
+ -- ancestralSwiftness = 443454,
+ -- heedMyCall = 443444,
+ -- routineCommunication = 443445,
+ -- primordialCapacity = 443448,
+ -- maelstromSupremacy = 443447,
+ -- elementalReverb = 443418,
+ -- spiritwalkersMomentum = 443425,
+ -- finalCalling = 443446,
+ },
+ totem = {
+ air = {
+ stormlashTotem = 120668,
+ windwalkTotem = 108273,
+ capacitorTotem = 108269,
+ groundingTotem = 8177,
+ },
+ earth = {
+ earthbindTotem = 2484,
+ earthElementalTotem = 2062,
+ earthquake = 61882,
+ tremorTotem = 8143,
+ },
+ fire = {
+ fireElementalTotem = 2894,
+ flameShock = 8050,
+ searingTotem = 3599,
+ magmaTotem = 8190,
+ },
+ water = {
+ healingStreamTotem = 5394,
+ healingTideTotem = 108280,
+ manaTideTotem = 16191,
+ },
+ },
+ },
+ },
+ WARLOCK = {
+ -- Affliction
+ [265] = {
+ abilities = {
+ agony = 980,
+ commanddemon = 119898,
+ corruption = 172,
+ darkSoul = 113860,
+ deathbolt = 264106,
+ demonicGateway = 311699,
+ drainLife = 234153,
+ drainSoul = 198590,
+ felDomination = 333889,
+ grimoireOfSacrifice = 108503,
+ haunt = 48181,
+ maleficRapture = 324536,
+ mortalCoil = 6789,
+ phantomSingularity = 205179,
+ reapSouls = 216698,
+ seedOfCorruption = 27243,
+ shadowBolt = 232670,
+ shadowBolt2 = 686,
+ shadowLock = 171138,
+ siphonLife = 63106,
+ spellLock = 19647,
+ spellLockgrimoire = 132409,
+ soulEffigy = 205178,
+ soulRot = 325640,
+ soulSwap = 386951,
+ soulTap = 387073,
+ summonDarkglare = 205180,
+ unstableAffliction = 30108,
+ vileTaint = 278350,
+ },
+ artifacts = {
+ },
+ buffs = {
+ cascadingCalamity = 275378,
+ compoundingHorror = 199281,
+ darkSoul = 113860,
+ deadwindHarvester = 216708,
+ demonicPower = 196099,
+ empoweredLifeTap = 235156,
+ felDomination = 333889,
+ nightfall = 264571,
+ tormentedSouls = 216695,
+ wrathOfConsumption = 199646,
+ inevitableDemise = 273525,
+ },
+ debuffs = {
+ agony = 980,
+ corruption = 146739,
+ haunt = 48181,
+ phantomSingularity = 205179,
+ seedOfCorruption = 27243,
+ siphonLife = 63106,
+ shadowEmbrace = 32390,
+ unstableAffliction = 316099,
+ unstableAffliction2 = 233496,
+ unstableAffliction3 = 233497,
+ unstableAffliction4 = 233498,
+ unstableAffliction5 = 233499,
+ vileTaint = 278350,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ absoluteCorruption = 196103,
+ creepingDeath = 264000,
+ darkCaller = 334183,
+ darkfury = 264874,
+ seedOfCorruption = 196226,
+ darkSoul = 113860,
+ demonicSacrifice = 108503,
+ drainSoul = 198590,
+ grimoireOfSacrifice = 108503,
+ haunt = 48181,
+ howlOfTerror = 5484,
+ inevitableDemise = 334319,
+ nightfall = 108558,
+ phantomSingularity = 205179,
+ siphonLife = 63106,
+ soulConduit = 215941,
+ sowTheSeeds = 196226,
+ vileTaint = 278350,
+ writheInAgony = 196102,
+ },
+ traits = {
+ cascadingCalamity = 275372,
+ dreadfulCalling = 278727,
+ inevitableDemise = 273521,
+ pandemicInvocation = 289364,
+ }
+ },
+ -- Demonology
+ [266] = {
+ abilities = {
+ axeToss = 89766,
+ bilescourgeBombers = 267211,
+ callDreadstalkers = 104316,
+ commandDemon = 119898,
+ corruption = 172,
+ demonbolt = 264178,
+ demonicEmpowerment = 193396,
+ demonicStrength = 267171,
+ demonwrath = 193440,
+ doom = 603,
+ drainLife = 234153,
+ felFirebolt = 104318,
+ felstorm = 89751,
+ grimoireFelguard = 111898,
+ guillotine = 386833,
+ handOfGuldan = 105174,
+ implosion = 196277,
+ netherPortal = 267217,
+ powerSiphon = 264130,
+ shadowBolt = 686,
+ shadowflame = 205181,
+ shadowLock = 171138,
+ shadowsBite = 387322,
+ spellLock = 19647,
+ summonDarkglare = 205180,
+ summonDemonicTyrant = 265187,
+ summonVilefiend = 264119,
+ thalkielsConsumption = 211714,
+ },
+ artifacts = {
+ thalkielsAscendance = 238145,
+ thalkielsConsumption = 211714,
+ },
+ buffs = {
+ demonicCalling = 205146,
+ demonicCore = 267102,
+ demonicEmpowerment = 193396,
+ demonicPower = 265273,
+ demonwrath = 193440,
+ explosivePotential = 275398,
+ forbiddenKnowledge = 278738,
+ netherPortal = 267218,
+ shadowsBite = 272945,
+ shadowyInspiration = 196269,
+ },
+ debuffs = {
+ doom = 603,
+ shadowflame = 205181,
+ corruption = 172,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ bilescourgeBombers = 267211,
+ darkfury = 264874,
+ demonicStrength = 267171,
+ demonicCalling = 205145,
+ doom = 603,
+ dreadlash = 264078,
+ fromTheShadows = 267170,
+ grimoireFelguard = 111898,
+ howlOfTerror = 5484,
+ innerDemons = 267216,
+ netherPortal = 267217,
+ powerSiphon = 264130,
+ sacrificedSouls = 267214,
+ summonVilefiend = 264119,
+ soulConduit = 215941,
+
+ },
+ traits = {
+ balefulInvocation = 287059,
+ explosivePotential = 275395,
+ shadowsBite = 272944,
+ }
+ },
+ -- Destruction
+ [267] = {
+ abilities = {
+ cataclysm = 152108,
+ channelDemonfire = 196447,
+ chaosBolt = 116858,
+ conflagrate = 17962,
+ corruption = 172,
+ commandDemon = 119898,
+ darkSoul = 113858,
+ devourMagic = 19505,
+ dimensionalRift = 196586,
+ drainLife = 234153,
+ felDomination = 333889,
+ grimoireOfSacrifice = 108503,
+ havoc = 80240,
+ immolate = 348,
+ incinerate = 29722,
+ rainOfFire = 5740,
+ shadowBolt = 686,
+ shadowburn = 17877,
+ shadowLock = 171138,
+ singeMagic = 119905,
+ singeMagicGrimoire = 132411,
+ soulFire = 6353,
+ spellLock = 19647,
+ spellLockgrimoire = 132409,
+ summonInfernal = 1122,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ backdraft = 117828, --196406,
+ crashingChaos = 277706,
+ darkSoul = 113858,
+ darkSoulInstability = 113858,
+ demonicPower = 196099,
+ empoweredLifeTap = 235156,
+ felDomination = 333889,
+ lessonsOfSpaceTime = 236174,
+ lordOfFlames = 224103,
+ },
+ debuffs = {
+ conflagrate = 265931,
+ eradication = 196414,
+ immolate = 157736,
+ havoc = 80240,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ cataclysm = 152108,
+ channelDemonfire = 196447,
+ darkFury = 264874,
+ darkSoul = 113858,
+ darkSoulInstability = 113858,
+ eradication = 196412,
+ flashover = 267115,
+ fireAndBrimstone = 196408,
+ grimoireOfSacrifice = 108503,
+ grimoireOfSupremacy = 266086,
+ howlOfTerror = 5484,
+ inferno = 270545,
+ internalCombustion = 266134,
+ reverseEntropy = 205148,
+ roaringBlaze = 205184,
+ shadowburn = 17877,
+ soulFire = 6353,
+ soulConduit = 215941,
+ },
+ traits = {
+ crashingChaos = 277644
+ }
+ },
+ -- Inital Warlock (1-10)
+ [1454] = {
+ abilities = {
+ controlDemon = 93375,
+ corruption = 172,
+ curseOfWeakness = 702,
+ drainLife = 234153,
+ shadowBolt = 686,
+ },
+ buffs = {
+
+ },
+ debuffs = {
+ corruption = 146739,
+ curseOfWeakness = 702,
+ drainLife = 234153,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ amplifyCurse = 328774,
+ banish = 710,
+ burningRush = 111400,
+ curseOfExhaustion = 334275,
+ curseOfTongues = 1714,
+ curseOfWeakness = 702,
+ createHealthstone = 6201,
+ createSoulwell = 29893,
+ commandDemon = 119898,
+ corruption = 172,
+ darkPact = 108416,
+ demonicGateway = 111771,
+ demonicCircle = 48018,
+ demonicTeleport = 48020,
+ devourMagic = 19505,
+ eyeOfKilrogg = 126,
+ felDomination = 333889,
+ fear = 5782,
+ grimoireFelhunter = 111897,
+ grimoireImp = 111859,
+ grimoireOfService = 108501,
+ grimoireSuccubus = 111896,
+ grimoireVoidwalker = 111895,
+ healthFunnel = 755,
+ inquisitorsGaze = 386344,
+ howlOfTerror = 5484,
+ lifeTap = 1454,
+ mortalCoil = 6789,
+ ritualOfDoom = 342601,
+ ritualOfSummoning = 698,
+ shadowflame = 384069,
+ shadowfury = 30283,
+ shadowBulwark = 119907,
+ spellLock = 19647,
+ seduction = 6358,
+ singeMagic = 89808,
+ soulburn = 385899,
+ soulHarvest = 196098,
+ soulstone = 20707,
+ summonSoulkeeper = 386256,
+ --summonDoomguard = 18540,
+ subjugateDemon = 1098,
+ summonFelguard = 30146,
+ summonFelhunter = 691,
+ summonFelImp = 688,
+ summonImp = 688,
+ summonInfernal = 1122,
+ summonSuccubus = 366222,
+ summonVoidwalker = 697,
+ summonWrathguard = 112870,
+ unendingBreath = 5697,
+ unendingResolve = 104773,
+ unstableaffliction = 316099,
+
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ burningRush = 111400,
+ demonicCircle = 48018,
+ demonicSynergy = 171982,
+ felDomination = 333889,
+ healthFunnel = 755,
+ grimoireOfSacrifice = 196099,
+ sindoreiSpite = 208871,
+ soulHarvest = 196098,
+ soulstone = 20707,
+ unendingBreath = 5697,
+ unendingResolve = 104773,
+ },
+ conduits = {
+ corruptingLeer = 339455,
+
+ },
+ covenants = {
+ decimatingBolt = 325289,
+ impendingCatastrophe = 321792,
+ scouringTithe = 312321,
+ },
+ debuffs = {
+ decimatingBolt = 325289,
+ impendingCatastrophe = 321792,
+ scouringTithe = 312321,
+ soulRot = 325640,
+ fear = 5782,
+ curseOfExhaustion = 334275,
+ curseOfTongues = 1714,
+ curseOfWeakness = 702,
+ },
+ glyphs = {
+ glyphOfTheFelImp = 219424,
+ },
+ talents = {
+ burningRush = 111400,
+ darkPact = 108416,
+ demonSkin = 219272,
+ mortalCoil = 6789,
+
+ },
+ },
+ },
+ WARRIOR = {
+ -- Arms
+ [71] = {
+ abilities = {
+ charge = 1250619,
+ colossusSmash = 86346,
+ dieByTheSword = 118038,
+ mortalStrike = 12294,
+ overpower = 7384,
+ slam = 1464,
+ sweepingStrikes = 12328,
+ whirlwind = 1680,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ -- ashenJuggernaut = 335234,
+ -- avatar = 389748,
+ -- battlelord = 346369,
+ -- battleStance = 386164,
+ -- bladestorm = 446035,
+ -- collateralDamage = 334783,
+ colossusSmash = 86346,
+ -- crushingAssault = 278826,
+ -- deadlyCalm = 262228,
+ -- defensiveStance = 386208,
+ -- exploiter = 335452,
+ -- imminentDemise = 445606,
+ -- inForTheKill = 215550,
+ -- juggernaut = 383290,
+ -- mercilessBonegrinder = 383316,
+ -- opportunist = 456120,
+ -- overpower = 400805,
+ -- recklessness = 389722,
+ -- stoneHeart = 225947,
+ -- suddenDeath = 52437,
+ suddenExecute = 139958,
+ sweepingStrikes = 12328,
+ tasteForBlood = 60503,
+ -- testOfMight = 275540,
+ },
+ conduits = {
+ -- ashenJuggernaut = 335232,
+ },
+ covenants = {
+ -- condemn = 317349,
+ -- condemnMassacre = 330334,
+ },
+ debuffs = {
+ colossusSmash = 108126,
+ -- deepWounds = 262115,
+ -- executionersPrecision = 272870,
+ -- markedForExecution = 445584,
+ -- hamstring = 1715,
+ -- ravager = 227876,
+ -- rend = 772,
+ },
+ glyphs = {
+ -- glyphOfThunderStrike = 68164,
+ },
+ talents = {
+ -- armoredToTheTeeth = 384124,
+ -- arterialBleed = 440995,
+ -- avatar = 107574,
+ -- barbaricTraining = 383082,
+ -- battleStance = 386164,
+ -- battlelord = 386630,
+ -- blademastersTorment = 390138,
+ -- bladestorm = 227847,
+ -- bloodborne = 383287,
+ -- bloodletting = 383154,
+ -- bloodsurge = 384361,
+ -- bluntInstruments = 383442,
+ -- boneshaker = 429639,
+ -- brutalFinish = 446085,
+ -- cleave = 845,
+ -- collateralDamage = 334779,
+ -- colossalMight = 429634,
+ -- colossusSmash = 167105,
+ -- criticalThinking = 389306,
+ -- crushingForce = 382764,
+ -- cullingCyclone = 444778,
+ -- danceOfDeath = 390713,
+ -- -- deadlyCalm = 262228,
+ -- deathDrive = 444770,
+ -- deftExperience = 389308,
+ -- demolish = 436358,
+ -- dieByTheSword = 118038,
+ -- dominanceOfTheColossus = 429636,
+ -- dreadnaught = 262150,
+ -- earthquaker = 440992,
+ -- enduranceTraining = 382940,
+ -- executionersPrecision = 386634,
+ -- exhilaratingBlows = 383219,
+ -- fatality = 383703,
+ -- fervorOfBattle = 202316,
+ -- fierceFollowthrough = 444773,
+ -- finishingBlows = 400205,
+ -- frothingBerserker = 392792,
+ -- fueledByViolence = 383103,
+ -- ignorePain = 190456,
+ -- imminentDemise = 444769,
+ -- impale = 383430,
+ -- improvedExecute = 316405,
+ -- improvedOverpower = 385571,
+ -- improvedSweepingStrikes = 383155,
+ -- inForTheKill = 248621,
+ -- juggernaut = 383292,
+ -- martialExpert = 429638,
+ -- martialProwess = 316440,
+ -- massacre = 281001,
+ -- mercilessBonegrinder = 383317,
+ -- mortalStrike = 12294,
+ -- mountainOfMuscleAndScars = 429642,
+ -- noStrangerToPain = 429644,
+ -- oneAgainstMany = 429637,
+ -- opportunist = 444774,
+ -- overpower = 7384,
+ -- overwhelmingBlades = 444772,
+ -- practicedStrikes = 429647,
+ -- preciseMight = 431548,
+ -- reapTheStorm = 444775,
+ -- relentlessPursuit = 444776,
+ -- rend = 772,
+ -- secondWind = 29838,
+ -- sharpenedBlades = 383341,
+ -- showNoMercy = 444771,
+ -- skullsplitter = 260643,
+ -- slayersDominance = 444767,
+ -- slayersMalice = 444779,
+ -- spitefulSerenity = 400314,
+ -- stormOfSwords = 385512,
+ -- stormWall = 388807,
+ -- strengthOfArms = 400803,
+ -- suddenDeath = 29725,
+ -- tactician = 184783,
+ -- testOfMight = 385008,
+ -- tideOfBattle = 429641,
+ -- twohandedWeaponSpecialization = 382896,
+ -- unhinged = 386628,
+ -- unrelentingOnslaught = 444780,
+ -- valorInVictory = 383338,
+ -- veteranVitality = 440993,
+ -- viciousAgility = 444777,
+ -- warMachine = 262231,
+ -- warbreaker = 262161,
+ -- warlordsTorment = 390140,
+ },
+ traits = {
+ -- seismicWave = 277639,
+ -- testOfMight = 275529,
+ }
+ },
+ -- Fury
+ [72] = {
+ abilities = {
+ crushingBlow = 335097,
+ enrage = 184361,
+ execute = 5308,
+ whirlwind = 190411,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ ashenJuggernaut = 392537,
+ avatar = 107574,
+ berserkerStance = 386196,
+ bladestorm = 446035,
+ bloodbath = 461288,
+ brutalFinish = 446918,
+ burstOfPower = 437121,
+ crushingBlow = 396752,
+ -- deathWish = 199261,
+ enrage = 184362,
+ enragedRegeneration = 184364,
+ -- frenzy = 335082,
+ meatCleaver = 85739,
+ -- mercilessBonegrinder = 346574,
+ opportunist = 456120,
+ recklessness = 1719,
+ suddenDeath = 280776,
+ -- whirlwind = 85739,
+ },
+ conduits = {
+
+ },
+ covenants = {
+ condemn = 317485,
+ condemnMassacre = 330325,
+ },
+ debuffs = {
+ championsMight = 386286,
+ markedForExecution = 445584,
+ odynsFuryTormentMh = 385060,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ armoredToTheTeeth = 384124,
+ ashenJuggernaut = 392536,
+ avatar = 107574,
+ avatarOfTheStorm = 437134,
+ barbaricTraining = 390674,
+ berserkerStance = 386196,
+ berserkersTorment = 390123,
+ bladestorm = 227847,
+ bloodborne = 385703,
+ bloodcraze = 393950,
+ bloodthirst = 23881,
+ brutalFinish = 446085,
+ burstOfPower = 437118,
+ coldSteelHotBlood = 383959,
+ crashingThunder = 436707,
+ criticalThinking = 383297,
+ cruelty = 392931,
+ crushingForce = 382764,
+ cullingCyclone = 444778,
+ dancingBlades = 391683,
+ deathDrive = 444770,
+ deftExperience = 383295,
+ depthsOfInsanity = 383922,
+ -- dragonRoar = 118000,
+ dualWieldSpecialization = 382900,
+ enduranceTraining = 391997,
+ enragedRegeneration = 184364,
+ fierceFollowthrough = 444773,
+ flashingSkies = 437079,
+ focusInChaos = 383486,
+ frenziedEnrage = 383848,
+ frenzy = 335077,
+ freshMeat = 215568,
+ frothingBerserker = 215571,
+ -- furiousCharge = 202224,
+ gatheringClouds = 436201,
+ groundCurrent = 436148,
+ hackAndSlash = 383877,
+ imminentDemise = 444769,
+ improvedBloodthirst = 383852,
+ improvedExecute = 316402,
+ improvedRagingBlow = 383854,
+ improvedWhirlwind = 12950,
+ invigoratingFury = 383468,
+ keepYourFeetOnTheGround = 438590,
+ lightningStrikes = 434969,
+ massacre = 206315,
+ meatCleaver = 280392,
+ odynsFury = 385059,
+ onslaught = 315720,
+ opportunist = 444774,
+ overwhelmingBlades = 444772,
+ powerfulEnrage = 440277,
+ ragingBlow = 85288,
+ rampage = 184367,
+ reapTheStorm = 444775,
+ recklessAbandon = 396749,
+ recklessness = 1719,
+ relentlessPursuit = 444776,
+ -- seethe = 335091,
+ showNoMercy = 444771,
+ -- siegebreaker = 280772,
+ singlemindedFury = 81099,
+ slaughteringStrikes = 388004,
+ slayersDominance = 444767,
+ slayersMalice = 444779,
+ snapInduction = 456270,
+ steadfastAsThePeaks = 434970,
+ stormBolts = 436162,
+ stormOfSteel = 382953,
+ stormShield = 438597,
+ strengthOfTheMountain = 437068,
+ suddenDeath = 280721,
+ swiftStrikes = 383459,
+ tenderize = 388933,
+ thorimsMight = 436152,
+ thunderBlast = 435607,
+ titansTorment = 390135,
+ titanicRage = 394329,
+ unbridledFerocity = 389603,
+ unhinged = 386628,
+ unrelentingOnslaught = 444780,
+ viciousAgility = 444777,
+ viciousContempt = 383885,
+ warMachine = 346002,
+ warpaint = 208154,
+ wrathAndFury = 392936,
+ },
+ traits = {
+ coldSteelHotBlood = 288080
+ }
+ },
+ -- Protection
+ [73] = {
+ abilities = {
+ demoralizingShout = 1160,
+ devastate = 20243,
+ focusedRage = 204488,
+ impendingVictory = 202168,
+ intercept = 198304,
+ lastStand = 12975,
+ ravager = 228920,
+ revenge = 6572,
+ shieldBlock = 2565,
+ shieldSlam = 23922,
+ shieldWall = 871,
+ shockwave = 46968,
+ thunderClap = 6343,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ avatar = 107574,
+ defensiveStance = 71,
+ lastStand = 12975,
+ revenge = 5302,
+ shieldBlock = 132404,
+ shieldWall = 871,
+ vengeanceFocusedRage = 202573,
+ vengeanceIgnorePain = 202574,
+ vengeanceRevenge = 202573,
+ victorious = 32216,
+ },
+ conduits = {
+
+ },
+ covenants = {
+ condemn = 317349,
+ },
+ debuffs = {
+ deepwoundsProt = 115767,
+ demoralizingShout = 1160,
+ thunderClap = 6343,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ bestServedCold = 202560,
+ warMachine = 316733,
+ bolster = 280001,
+ boomingVoice = 202743,
+ cracklingThunder = 203201,
+ devastator = 236279,
+ dragonRoar = 118000,
+ heavyRepercussions = 203177,
+ indomitable = 202095,
+ intoTheFray = 202603,
+ menace = 275338,
+ neverSurrender = 202561,
+ punish = 275334,
+ ravager = 228920,
+ rumblingEarth = 275339,
+ unstoppableForce = 275336,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ attack = 88163,
+ battleShout = 6673,
+ battleStance = 2457,
+ berserkerRage = 18499,
+ berserkerStance = 2458,
+ charge = 100,
+ cleave = 845,
+ commandingShout = 469,
+ defensiveStance = 71,
+ demoralizingBanner = 114203,
+ disarm = 676,
+ execute = 5308,
+ hamstring = 1715,
+ heroicLeap = 6544,
+ heroicStrike = 78,
+ heroicThrow = 57755,
+ intervene = 3411,
+ intimidatingShout = 5246,
+ mockingBanner = 114192,
+ pummel = 6552,
+ rallyingCry = 97462,
+ recklessness = 1719,
+ -- shieldBlock = 2565,
+ -- shieldSlam = 23922,
+ shatteringThrow = 64382,
+ shieldWall = 871,
+ skullBanner = 114207,
+ spellReflection = 23920,
+ sunderArmor = 7386,
+ taunt = 355,
+ throw = 122475,
+ thunderClap = 6343,
+ victoryRush = 34428,
+ -- whirlwind = 1680,
+ },
+ animas = {
+ -- bloodstainedWhetstone = 322036,
+ -- hurricaneHeart = 329122,
+ -- championsDecree = 322054,
+ -- periaptOfFuror = 322049,
+ -- pleonexianCommand = 329165,
+ -- rageMote = 322046,
+ -- weatheredRunestone = 349916,
+ -- singingStones = 329489,
+ -- singingStonesOfIntimidation = 329484,
+ -- singingStonesOfUnnerving = 329488,
+ -- tarnishedMedallion = 348483,
+ -- thunderingRoar = 322093,
+ -- zovaalsWarbanner = 322027,
+ -- bladedBulwark = 322026,
+ -- fanOfLongswords = 322029,
+ -- misshapenMirror = 329268,
+ -- shriekingFlagon = 329275,
+ -- voraciousCullingBlade = 329213,
+ -- kyrianWarhelm = 314290,
+ -- scratchedKnife = 322039,
+ -- signetOfTormentedKings = 329345,
+ -- smolderingInertia = 322032,
+ -- umbralEarTrumpet = 329283,
+ -- warlordsResolve = 322051,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ battleShout = 6673,
+ -- berserkerRage = 18499,
+ bloodbath = 12292,
+ defensiveStance = 71,
+ enrage = 12880,
+ -- firstStrike = 325381,
+ -- fujiedasFury = 207776,
+ -- ignorePain = 190456,
+ -- outburst = 364010,
+ recklessness = 1719,
+ -- seeingRed = 364006,
+ skullBanner = 114207,
+ -- smolderingInertia = 322032,
+ -- spellReflection = 23920,
+ victorious = 32216,
+ },
+ conduits = {
+ -- viciousContempt = 337302,
+ -- destructiveReverberations = 339939,
+ },
+ covenants = {
+ -- ancientAftershock = 325886,
+ -- conquerorsBanner = 324143,
+ -- fleshcraft = 324631,
+ -- spearOfBastion = 307865,
+ },
+ debuffs = {
+ deepWounds = 115768,
+ disarm = 116095,
+ -- markOfFyralath = 414532,
+ },
+ glyphs = {
+
+ },
+ items = {
+ -- treacherousTransmitter = 221023,
+ },
+ runeforges = {
+ -- battlelord = 335274,
+ -- cadenceOfFujieda = 335555,
+ -- deathmaker = 335567,
+ -- elysianMight = 357996,
+ -- enduringBlow = 335458,
+ -- exploiter = 335451,
+ -- leaper = 335214,
+ -- misshapenMirror = 335253,
+ -- recklessDefence = 335582,
+ -- reprisal = 335718,
+ -- seismicReverberation = 335758,
+ -- signetOfTormentedKings = 335266,
+ -- sinfulSurge = 354131,
+ -- theWall = 335239,
+ -- thunderlord = 335229,
+ -- unhinged = 335282,
+ -- willOfTheBerserker = 335594,
+ -- unbreakableWill = 335629,
+ },
+ talents = {
+ avatar = 107574,
+ bladestorm = 46924,
+ bloodbath = 12292,
+ doubleTime = 103827,
+ disruptingShout = 102060,
+ dragonRoar = 118000,
+ enragedRegeneration = 55694,
+ impendingVictory = 103840,
+ juggernaut = 103826,
+ massSpellReflection = 114028,
+ piercingHowl = 12323,
+ safeguard = 114029,
+ secondWind = 29838,
+ shockwave = 46968,
+ staggeringShout = 107566,
+ stormBolt = 107570,
+ vigilance = 114030,
+ warbringer = 103828,
+ },
+ },
+ },
+ -- Global
+ Shared = {
+ Shared = {
+ abilities = {
+ autoAttack = 6603,
+ autoShot = 75,
+ giftOfTheNaaru = br.functions.spell:getRacial("Draenei"), --select(7, GetSpellInfo(GetSpellInfo(28880))),
+ global = 61304,
+ -- latentArcana = 296971,
+ -- lightsJudgment = 255647,
+ quakingPalm = br.functions.spell:getRacial("Pandaren"),
+ racial = br.functions.spell:getRacial(),
+ shadowmeld = br.functions.spell:getRacial("NightElf"),
+ twoForms = 68996,
+ },
+ animas = {
+ -- scrollOfElchaver = 305006,
+ },
+ artifacts = {
+ -- artificialDamage = 226829,
+ -- artificialStamina = 211309,
+ -- concordanceOfTheLegionfall = 239042,
+ },
+ buffs = {
+ ancientHysteria = 90355,
+ -- battlePotionOfAgility = 279152,
+ -- battlePotionOfIntellect = 279151,
+ -- battlePotionOfStrength = 279153,
+ -- superiorBattlePotionOfAgility = 298146,
+ -- potionOfUnbridledFury = 300714,
+ blessingOfSacrifice = 6940,
+ -- greaterBlessingOfKings = 203538,
+ -- greaterBlessingOfWisdom = 203539,
+ -- battleScarredAugmentation = 270058, -- BfA Augment Rune Buff
+ blessingOfFreedom = 1044,
+ blessingOfProtection = 1022,
+ bloodLust = {
+ ancientHysteria = 90355,
+ bloodlust = 2825,
+ drumsOfRage = 146555,
+ -- drumsOfTheMaelstrom = 256740,
+ -- drumsOfTheMountain = 230935,
+ heroism = 32182,
+ -- netherwinds = 160452,
+ -- primalRage = 264667,
+ timewarp = 80353,
+ },
+ -- concordanceOfTheLegionfall = 239042,
+ -- defiledAugmentation = 224001, -- Lightforged Augment Rune buff
+ -- felFocus = 242551,
+ flashOfSpringBlossoms = 105689,
+ -- flaskOfTenThousandScars = 188035,
+ -- flaskOfTheCountlessArmies = 188034,
+ -- flaskOfTheSeventhDemon = 188033,
+ -- flaskOfTheWhisperedPact = 188031,
+ -- flaskOfTheCurrents = 251836,
+ -- flaskOfEndlessFathoms = 251837,
+ -- flaskOfTheVastHorizon = 251838,
+ -- flaskOfTheUndertow = 251839,
+ -- flayedWingToxin = 345546,
+ -- fruitfulMachinatins = 242623, -- Absorb Shield from Deceiver's Grand Design
+ -- mistcallerOcarina = 330067, -- SL Trinket with group buff
+ -- greaterFlaskOfEndlessFathoms = 298837,
+ -- greaterFlaskOfTheCurrents = 298836,
+ -- greaterFlaskOfTheUndertow = 298841,
+ -- greaterFlaskOfTheVastHorizon = 298839,
+ -- guidingHand = 242622, -- from The Deceiver's Grand Design
+ heroism = 32182,
+ -- ineffableTruth = 316801,
+ -- latentArcana = 296962,
+ -- netherwinds = 160452,
+ -- norgannonsForesight = 236373,
+ -- overchargeMana = 299624,
+ -- potionOfBurstingBlood = 265514,
+ -- potionOfFocusedResolve = 298317,
+ -- prolongedPower = 229206,
+ racial = br.functions.spell:getRacial(),
+ -- razorCoral = 303570, -- Crit Buff from Ashvane's Razor Coral
+ -- sephuz1 = 208051, -- the fulltime 10% movement, 2% haste buff
+ -- sephuz2 = 208052, -- the proc, 70% movement, 25% haste buff
+ -- sephuzCooldown = 226262, -- CD (30 seconds) for the proc
+ shadowmeld = 58984,
+ symbolOfHope = 64901,
+ timeWarp = 80353,
+ -- whispersOfInsanity = 176151,
+ -- cracklingTourmaline = 290372,
+ -- saphireofBrilliance = 290365,
+ -- vigorEngaged = 287916,
+ -- soulshape = 310143,
+ -- soulIgnition = 345251,
+ -- spectralFlaskOfPower = 307185,
+ -- elementalPotionOfPower = 371024,
+ -- elementalPotionOfUltimatePower = 371028,
+ -- phialOfGlacialFury = 373257,
+ -- phialOfTepidVersatility = 371172,
+ -- icedPhialOfCorruptingRage = 374000,
+ -- domineeringArrogance = 411661,
+ runeOfReorigination = 139120,
+ vicious = 148903,
+ virmensBite = 105697,
+ },
+ --TODO Add to API
+ itemEnchantments = {
+ --Dragonflight weapon Imbue's from inscription enchantments
+ --Quality of aura should always be GOLD,SILVER,COPPER
+ -- buzzingRune = { 6514, 6513, 6512 },
+ -- chirpingRune = { 6695, 6694, 6515 },
+ -- howlingRune = { 6518, 6517, 6516 },
+ -- hissingRune = { 6839, 6837, 6838 },
+ },
+ conduits = {
+
+ },
+ covenants = {
+ -- covenantAbility = 313347,
+ -- doorOfShadows = 300728,
+ -- fleshcraft = 324631,
+ -- soulshape = 310143,
+ -- flicker = 324701,
+ -- summonSteward = 324739,
+ },
+ debuffs = {
+ -- bloodOfTheEnemy = 297108,
+ -- concentratedFlame = 295368,
+ -- conductiveInk = 302565,
+ convert = 122740,
+ dampening = 110310,
+ -- eyeOfLeotheras = 206649,
+ -- necroticWound = 209858,
+ -- razorCoral = 303568, --304877,
+ -- repeatPerformance = 304409,
+ -- shiverVenom = 301624,
+ -- temptation = 234143,
+ -- eyeOfCorruption = 315161,
+ -- grandDelusions = 319695,
+ -- graspingTendrils = 315176,
+ -- creepingMadness = 313255,
+ -- fixate = 318078,
+ -- thirstForBlood = 266107,
+ mightyBash = 5211,
+ -- defiledGround = 314565,
+ -- vileCorruption = 314392,
+ -- timeToFeed = 162415, -- Oshir Iron Docks
+ -- cascadingTerror = 314478,
+ },
+ essences = {
+ },
+ },
+ },
+}
diff --git a/System/Lists/Expansions/Retail/Spells.lua b/System/Lists/Expansions/Retail/Spells.lua
new file mode 100644
index 0000000000..eaef3d93cb
--- /dev/null
+++ b/System/Lists/Expansions/Retail/Spells.lua
@@ -0,0 +1,5526 @@
+local _, br = ...
+
+if br.lists == nil then br.lists = {} end
+
+-- Racial spell IDs for this expansion.
+-- Defined before br.lists.spells so getRacial() can read them during spell list construction.
+-- Three races use dynamic IDs resolved at runtime; their base lookup IDs are stored with a _ prefix.
+br.lists.racials = {
+ -- Alliance
+ Dwarf = 20594, -- Stoneform
+ Gnome = 20589, -- Escape Artist
+ Human = 59752, -- Every Man for Himself
+ NightElf = 20580, -- Shadowmeld
+ Worgen = 68992, -- Darkflight
+ -- Horde
+ Goblin = 69041, -- Rocket Barrage
+ Tauren = 20549, -- War Stomp
+ Troll = 26297, -- Berserking
+ Scourge = 7744, -- Will of the Forsaken
+ -- Both
+ Pandaren = 107079, -- Quaking Palm
+ -- Allied Races
+ HighmountainTauren = 255654, -- Bull Rush
+ LightforgedDraenei = 255647, -- Light's Judgment
+ Nightborne = 260364, -- Arcane Pulse
+ VoidElf = 256948, -- Spatial Rift
+ DarkIronDwarf = 265221, -- Fireblood
+ MagharOrc = 274738, -- Ancestral Call
+ ZandalariTroll = 291944, -- Regeneratin'
+ KulTiran = 287712, -- Haymaker
+ Vulpera = 312411, -- Bag of Tricks
+ Mechagnome = 312924, -- Hyper Organic Light Originator
+ -- Base lookup IDs for dynamically-resolved racials (Arcane Torrent, Gift of the Naaru, Blood Fury)
+ _bloodElfBase = 69179,
+ _draeneiBase = 28880,
+ _orcBase = 33702,
+}
+
+br.lists.spells = {
+ DEATHKNIGHT = {
+ -- Blood
+ [250] = {
+ abilities = {
+ deathsCaress = 195292,
+ },
+ artifacts = {
+ allConsumingRot = 192464,
+ bloodFeast = 192548,
+ bonebreaker = 192538,
+ coagulopathy = 192460,
+ consumption = 205223,
+ danceOfDarkness = 192514,
+ grimPerseverance = 192447,
+ ironHeart = 192450,
+ meatShield = 192453,
+ mouthOfHell = 192570,
+ rattlingBones = 192557,
+ sanguinaryAffinity = 221775,
+ skeletalShattering = 192558,
+ theHungeringMaw = 214903,
+ umbilicusEternus = 193213,
+ unendingThirst = 192567,
+ veinrender = 192457,
+ },
+ buffs = {
+ boneShield = 195181,
+ bloodShield = 77535,
+ coagulopathy = 391477,
+ crimsonScourge = 81136,
+ dancingRuneWeapon = 81256,
+ hemostasis = 273947,
+ ossuary = 219786,
+ runeTap = 194679,
+ tombstone = 219809,
+ vampiricBlood = 55233,
+ vampiricStrength = 408356,
+ },
+ debuffs = {
+ asphyxiate = 221562,
+ blooddrinker = 206931,
+ heartStrike = 206930,
+ markOfBlood = 206940,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ antimagicBarrier = 205727,
+ bloodBoil = 50842,
+ bloodFeast = 391386,
+ bloodTap = 221699,
+ blooddrinker = 206931,
+ bloodiedBlade = 458753,
+ bloodshot = 391398,
+ bloodworms = 195679,
+ boneCollector = 458572,
+ bonestorm = 194844,
+ carnage = 458752,
+ coagulopathy = 391477,
+ consumption = 274156,
+ dancingRuneWeapon = 49028,
+ everlastingBond = 377668,
+ foulBulwark = 206974,
+ gorefiendsGrasp = 108199,
+ gripOfTheDead = 273952,
+ heartStrike = 206930,
+ heartbreaker = 221536,
+ heartrend = 377655,
+ hemostasis = 273946,
+ improvedBoneShield = 374715,
+ improvedHeartStrike = 374717,
+ improvedVampiricBlood = 317133,
+ insatiableBlade = 377637,
+ ironHeart = 391395,
+ leechingStrike = 377629,
+ markOfBlood = 206940,
+ marrowrend = 195182,
+ ossifiedVitriol = 458744,
+ ossuary = 219786,
+ perseveranceOfTheEbonBlade = 374747,
+ purgatory = 114556,
+ rapidDecomposition = 194662,
+ redThirst = 205723,
+ reinforcedBones = 374737,
+ relishInBlood = 317610,
+ runeTap = 194679,
+ sanguineGround = 391458,
+ shatteringBone = 377640,
+ tighteningGrasp = 206970,
+ tombstone = 219809,
+ umbilicusEternus = 391517,
+ vampiricBlood = 55233,
+ voracious = 273953,
+ willOfTheNecropolis = 206967,
+ },
+ },
+ -- Frost
+ [251] = {
+ abilities = {
+ empowerRuneWeapon = 47568,
+ frostStrike = 49143,
+ frostwyrmsFury = 279302,
+ howlingBlast = 49184,
+ obliterate = 49020,
+ pillarOfFrost = 51271,
+ remorselessWinter = 196770,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ breathOfSindragosa = 155166,
+ coldHeart = 281210,
+ darkSuccor = 101568,
+ empowerRuneWeapon = 47568,
+ eradicatingBlow = 337936,
+ frozenPulse = 195750,
+ icyCitadel = 272723,
+ icyTalons = 194879,
+ killingMachine = 51124,
+ pillarOfFrost = 51271,
+ remorselessWinter = 196770,
+ rime = 59052,
+ unleashedFrenzy = 338501,
+ },
+ conduits = {
+ eradicatingBlow = 337934,
+ everfrost = 337988,
+ unleashedFrenzy = 338492,
+ },
+ debuffs = {
+ breathOfSindragosa = 155166,
+ remorselessWinter = 196771,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ asphyxiate = 108194,
+ avalanche = 207142,
+ breathOfSindragosa = 152279,
+ coldHeart = 281208,
+ frostscythe = 207230,
+ frozenPulse = 194909,
+ gatheringStorm = 194912,
+ glacialAdvance = 194913,
+ hornOfWinter = 57330,
+ hypothermicPresence = 321995,
+ icecap = 207126,
+ inexorableAssault = 253593,
+ murderousEffeciency = 207061,
+ obliteration = 281238,
+ },
+ traits = {
+ frozenTempest = 278487,
+ icyCitadel = 272718,
+ }
+ },
+ -- Unholy
+ [252] = {
+ abilities = {
+ apocalypse = 275699,
+ armyOfTheDead = 42650,
+ darkTransformation = 63560,
+ epidemic = 207317,
+ festeringStrike = 85948,
+ outbreak = 77575,
+ scourgeStrike = 55090,
+ -- Pet Abilities
+ claw = 47468,
+ gnaw = 47481,
+ huddle = 47484,
+ hook = 212468,
+ leap = 47482,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ corpseShield = 207319,
+ darkSuccor = 101568,
+ darkTransformation = 63560,
+ festermight = 274373,
+ huddle = 91838,
+ necrosis = 207346,
+ soulReaper = 215711,
+ suddenDoom = 49530,
+ runicCorruption = 51460,
+ unholyAssault = 207289,
+ unholyBlight = 115989,
+ empowerRuneWeapon = 47568,
+ commanderOfTheDead = 390259,
+ summonGargoyle = 61777,
+ },
+ conduits = {
+ convocationOfTheDead = 338553,
+ },
+ debuffs = {
+ festeringWound = 194310,
+ unholyBlight = 115994,
+ virulentPlague = 191587,
+ rottenTouch = 390275,
+ deathRot = 377537,
+
+ },
+ glyphs = {
+
+ },
+ talents = {
+ allWillServe = 194916,
+ armyOfTheDamned = 276837,
+ burstingSores = 207264,
+ clawingShadows = 207311,
+ defile = 152280,
+ ebonFever = 207269,
+ gripOfTheDead = 273952,
+ harbingerOfDoom = 276023,
+ infectedClaws = 207272,
+ pestilence = 277234,
+ summonGargoyle = 49206,
+ unholyAssault = 207289,
+ unholyBlight = 115989,
+ unholyPact = 319230,
+ wraithWalk = 212552,
+ commanderOfTheDead = 390259,
+ deathRot = 377537,
+ improvedDeathCoil = 377580,
+ coilOfDevastation = 390270,
+ festermight = 377590,
+ rottenTouch = 390275,
+ vileContagion = 390279,
+ },
+ traits = {
+ magusOfTheDead = 288417,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+
+ antiMagicShell = 48707,
+ chainsOfIce = 45524,
+ darkCommand = 56222,
+ deathAndDecay = 43265,
+ deathCoil = 47541,
+ deathGate = 50977,
+ deathGrip = 49576,
+ deathsAdvance = 48265,
+ lichborne = 49039,
+ desecratedGround = 108201,
+ pathOfFrost = 3714,
+ raiseAlly = 61999,
+ runeforging = 53428,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ antiMagicShell = 48707,
+ coldHeartItem = 235599,
+ deathAndDecay = 188290,
+ deathsDue = 324164,
+ iceboundFortitude = 48792,
+ icyTalons = 194878,
+ pathOfFrost = 3714,
+ unholyStrength = 53365,
+ wraithWalk = 212552,
+ },
+ covenants = {
+ abominationLimb = 315443,
+ deathsDue = 324128,
+ shackleTheUnworthy = 312202,
+ swarmingMist = 311648,
+ },
+ debuffs = {
+ bloodPlague = 55078,
+ chainsOfIce = 45524,
+ controlUndead = 111673,
+ darkCommand = 56222,
+ frostBreath = 190780,
+ frostFever = 55095,
+ razorice = 51714,
+ soulReaper = 343294,
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+ bitingCold = 334678,
+ deadliestCoil = 334949,
+ phearomones = 335177,
+ superstrain = 334974,
+ },
+ talents = {
+ runeStrike = 316239,
+ abominationLimb = 383269,
+ antimagicZone = 51052,
+ asphyxiate = 221562,
+ assimilation = 374383,
+ blindingSleet = 207167,
+ bloodDraw = 374598,
+ bloodScent = 374030,
+ brittle = 374504,
+ cleavingStrikes = 316916,
+ coldthirst = 378848,
+ controlUndead = 111673,
+ deathStrike = 49998,
+ deathPact = 48743,
+ deathsEcho = 356367,
+ deathsReach = 276079,
+ enfeeble = 392566,
+ gloomWard = 391571,
+ icePrison = 454786,
+ iceboundFortitude = 48792,
+ icyTalons = 194878,
+ improvedDeathStrike = 374277,
+ insidiousChill = 391566,
+ marchOfDarkness = 391546,
+ mindFreeze = 47528,
+ nullMagic = 454842,
+ osmosis = 454835,
+ permafrost = 207200,
+ proliferatingChill = 373930,
+ raiseDead = 46585,
+ runeMastery = 374574,
+ runicAttenuation = 207104,
+ runicProtection = 454788,
+ sacrificialPact = 327574,
+ soulReaper = 343294,
+ subduingGrasp = 454822,
+ suppression = 374049,
+ unholyBond = 374261,
+ unholyEndurance = 389682,
+ unholyGround = 374265,
+ unyieldingWill = 457574,
+ vestigialShell = 454851,
+ veteranOfTheThirdWar = 48263,
+ wraithWalk = 212552,
+ },
+ talentsHeroic = {
+ -- Deathbringer
+ bindInDarkness = 440031,
+ bloodFever = 440002,
+ darkTalons = 436687,
+ deathsMessenger = 437122,
+ expellingShield = 439948,
+ exterminate = 441378,
+ grimReaper = 434905,
+ pactOfTheDeathbringer = 440476,
+ painfulDeath = 443564,
+ reapersMark = 439843,
+ runeCarvedPlates = 440282,
+ soulRupture = 437161,
+ swiftEnd = 443560,
+ waveOfSouls = 439851,
+ witherAway = 441894,
+ -- Rider of the Apocalypse
+ aFeastOfSouls = 444072,
+ apocalypseNow = 444040,
+ deathCharge = 444010,
+ furyOfTheHorsemen = 444069,
+ horsemansAid = 444074,
+ hungeringThirst = 444037,
+ mawswornMenace = 444099,
+ mograinesMight = 444047,
+ nazgrimsConquest = 444052,
+ onAPalerHorse = 444008,
+ pactOfTheApocalypse = 444083,
+ ridersChampion = 444005,
+ trollbanesIcyFury = 444097,
+ whitemanesFamine = 444033,
+ -- Sanlayn
+ bloodsoakedGround = 434033,
+ bloodyFortitude = 434136,
+ frenziedBloodthirst = 434075,
+ giftOfTheSanlayn = 434152,
+ inciteTerror = 434151,
+ inflictionOfSorrow = 434143,
+ newlyTurned = 433934,
+ pactOfTheSanlayn = 434261,
+ sanguineScent = 434263,
+ theBloodIsLife = 434260,
+ vampiricAura = 434100,
+ vampiricSpeed = 434028,
+ vampiricStrike = 433901,
+ visceralStrength = 434157,
+ }
+ },
+ },
+ DEMONHUNTER = {
+ -- Havoc
+ [577] = {
+ abilities = {
+ annihilation = 201427,
+ bladeDance = 188499,
+ blur = 198589,
+ chaosStrike = 162794,
+ deathSweep = 210152,
+ demonsBite = 162243,
+ felRush = 195072,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ furiousGaze = 343312,
+ initiative = 391215,
+ innerDemon = 337313,
+ metamorphosis = 162264,
+ momentum = 208628,
+ potion = 371024,
+ prepared = 203650,
+ unboundChaos = 347462, --234059,
+ },
+ conduits = {
+ serratedGlaive = 339230,
+ },
+ debuffs = {
+ essenceBreak = 320338,
+ exposedWound = 339229,
+ },
+ glyphs = {
+
+ },
+ items = {
+ algetharPuzzleBox = 193701,
+ augmentation = 201325,
+ beaconToTheBeyond = 203963,
+ dragonfireBombDispenser = 202610,
+ elementiumPocketAnvil = 202617,
+ irideusFragment = 193743,
+ manicGrieftorch = 194308,
+ potion = 191381,
+ stormeatersBoon = 194302,
+ },
+ talents = {
+ aFireInside = 427775,
+ acceleratedBlade = 391275,
+ anyMeansNecessary = 388114,
+ blindFury = 203550,
+ burningHatred = 320374,
+ burningWound = 391189,
+ chaosTheory = 389687,
+ chaoticDisposition = 428492,
+ chaoticTransformation = 388112,
+ criticalChaos = 320413,
+ cycleOfHatred = 258887,
+ dancingWithFate = 389978,
+ dashOfChaos = 427794,
+ deflectingDance = 427776,
+ demonBlades = 203555,
+ demonHide = 428241,
+ desperateInstincts = 205411,
+ essenceBreak = 258860,
+ eyeBeam = 198013,
+ felBarrage = 258925,
+ felblade = 232893,
+ firstBlood = 206416,
+ furiousGaze = 343311,
+ furiousThrows = 393029,
+ glaiveTempest = 342817,
+ growingInferno = 390158,
+ improvedChaosStrike = 343206,
+ improvedFelRush = 343017,
+ inertia = 427640,
+ initiative = 388108,
+ innerDemon = 389693,
+ insatiableHunger = 258876,
+ isolatedPrey = 388113,
+ knowYourEnemy = 388118,
+ looksCanKill = 320415,
+ momentum = 206476,
+ mortalDance = 328725,
+ netherwalk = 196555,
+ ragefire = 388107,
+ relentlessOnslaught = 389977,
+ restlessHunter = 390142,
+ scarsOfSuffering = 428232,
+ serratedGlaive = 390154,
+ shatteredDestiny = 388116,
+ soulscar = 388106,
+ tacticalRetreat = 389688,
+ trailOfRuin = 258881,
+ unboundChaos = 347461,
+ },
+ traits = {
+ chaoticTransformation = 288754,
+ eyesOfRage = 278500,
+ furiousGaze = 273231,
+ revolvingBlades = 279581,
+ unboundChaos = 275144,
+ },
+ },
+ -- Vengeance
+ [581] = {
+ abilities = {
+ demonSpikes = 203720,
+ --felDevastation = 212084,
+ --fieryBrand = 204021,
+ immolationAura = 258920,
+ infernalStrike = 189110,
+ metamorphosis = 187827,
+ shear = 203782,
+ --sigilOfChains = 202138,
+ --sigilOfMisery = 207684,
+ soulCleave = 228477,
+ throwGlaive = 204157,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ bladeTurning = 247254,
+ demonSpikes = 203819,
+ empowerWards = 218256,
+ feastofSouls = 207693,
+ felBombardment = 337775,
+ metamorphosis = 187827,
+ soulFragments = 203981,
+ siphonedPower = 218561,
+ },
+ debuffs = {
+ fieryBrand = 207744,
+ frailty = 247456,
+ },
+ glyphs = {
+
+ },
+ items = {
+ augmentation = 201325,
+ potion = 191383,
+ },
+ talents = {
+ agonizingFlames = 207548,
+ ascendingFlame = 428603,
+ auraOfPain = 207347,
+ bulkExtraction = 320341,
+ burningAlive = 207739,
+ burningBlood = 390213,
+ calcifiedSpikes = 389720,
+ chainsOfAnger = 389715,
+ charredFlesh = 336639,
+ charredWarblades = 213010,
+ cycleOfBinding = 389718,
+ darkglareBoon = 389708,
+ deflectingSpikes = 321028,
+ downInFlames = 389732,
+ extendedSpikes = 389721,
+ fallout = 227174,
+ feastofSouls = 207697,
+ feedTheDemon = 218612,
+ felDevastation = 212084,
+ felFlameFortification = 389705,
+ felblade = 232893,
+ fieryBrand = 204021,
+ fieryDemise = 389220,
+ focusedCleave = 343207,
+ fracture = 263642,
+ frailty = 389958,
+ illuminatedSigils = 428557,
+ infernalArmor = 320331,
+ lastResort = 209258,
+ meteoricStrikes = 389724,
+ painbringer = 207387,
+ perfectlyBalancedGlaive = 320387,
+ quickenedSigils = 209281,
+ retaliation = 389729,
+ revelInPain = 343014,
+ roaringFire = 391178,
+ ruinousBulwark = 326853,
+ shearFury = 389997,
+ sigilOfChains = 202138,
+ sigilOfSilence = 202137,
+ soulBarrier = 263648,
+ soulCarver = 207407,
+ soulFurnace = 391165,
+ soulCrush = 389985,
+ soulmonger = 389711,
+ spiritBomb = 247454,
+ stokeTheFlames = 393827,
+ voidReaver = 268175,
+ volatileFlameblood = 390808,
+ vulnerability = 389976,
+ },
+ },
+ -- Initial Demon Hunter 8-10
+ [1456] = {
+ abilities = {
+ chaosStrike = 344862, --162794,
+ demonsBite = 344859, --162243,
+ felRush = 344865, --195072,
+ sigilOfFlame = 204596,
+ },
+ buffs = {
+
+ },
+ debuffs = {
+ sigilOfFlame = 204598,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ chaosStrike = 344862,
+ demonsBite = 344859,
+ disrupt = 183752,
+ felRush = 344865,
+ glide = 131347,
+ immolationAura = 258920,
+ metamorphosis = 191427,
+ sigilOfFlame = 204596,
+ soulCarver = 214743,
+ spectralSight = 188501,
+ throwGlaive = 185123,
+ torment = 185245,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ chaosTheory = 337567,
+ felBarrage = 258925,
+ felBombardment = 337849,
+ felCrystalInfusion = 193547,
+ gazeOfTheLegion = 193456,
+ glide = 131347,
+ immolationAura = 258920,
+ inertia = 427641,
+ tacticalRetreat = 389890,
+ twistedHeart = 396046,
+ },
+ covenants = {
+ -- elysianDecree = 306830,
+ -- fodderToTheFlame = 329554,
+ -- sinfulBrand = 317009,
+ -- theHunt = 323639,
+ },
+ debuffs = {
+ burningWound = 346278,
+ essenceBreak = 320338,
+ sigilOfFlame = 204598,
+ sinfulBrand = 317009,
+ },
+ glyphs = {
+ glyphOfCracklingFlames = 219831,
+ glyphOfFallowWings = 220083,
+ glyphOfFearsomeMetamorphosis = 220831,
+ glyphOfFelTouchedSouls = 219713,
+ glyphOfFelWings = 220228,
+ glyphOfFelEnemies = 220240,
+ glyphOfManaTouchedSouls = 219744,
+ glyphOfShadowEnemies = 220244,
+ glyphOfTatteredWings = 220226,
+ },
+ runeforges = {
+ agonyGaze = 355886,
+ burningWound = 346279,
+ chaosTheory = 337551,
+ darkglareMedallion = 337534,
+ felBombardment = 337775,
+ razelikhsDefilement = 337544,
+ },
+ talents = {
+ aldrachiDesign = 391409,
+ blazingPath = 320416,
+ bouncingGlaives = 320386,
+ championOfTheGlaive = 429211,
+ chaosFragments = 320412,
+ chaosNova = 179057,
+ collectiveAnguish = 390152,
+ consumeMagic = 278326,
+ darkness = 196718,
+ demonic = 213410,
+ demonMuzzle = 388111,
+ elysianDecree = 390163,
+ erraticFelheart = 391397,
+ felfireHaste = 389846,
+ flamesOfFury = 389694,
+ illidariKnowledge = 389696,
+ imprison = 217832,
+ improvedDisrupt = 320361,
+ improvedSigilOfMisery = 320418,
+ internalStruggle = 393822,
+ liveByTheGlaive = 428607,
+ longNight = 389781,
+ lostInDarkness = 389849,
+ masterOfTheGlaive = 389763,
+ pitchBlack = 389783,
+ preciseSigils = 389799,
+ pursuit = 320654,
+ rushOfChaos = 320421,
+ shatteredRestoration = 389824,
+ sigilOfMisery = 207684,
+ soulRending = 204909,
+ soulSigils = 395446,
+ swallowedAnger = 320313,
+ theHunt = 370965,
+ unrestrainedFury = 320770,
+ vengefulBonds = 320635,
+ vengefulRetreat = 198793,
+ willOfTheIllidari = 389695,
+ },
+ },
+ },
+ DRUID = {
+ -- Initial
+ [1447] = {
+
+ },
+ -- Balance
+ [102] = {
+ abilities = {
+ celestialAlignment = 194223,
+ fullMoon = 274283,
+ furyOfElune = 202770,
+ halfMoon = 274282,
+ innervate = 29166,
+ starfire = 194153,
+ newMoon = 274281,
+ removeCorruption = 2782,
+ solarBeam = 78675,
+ typhoon = 132469,
+ wrath = 190984,
+ starfall = 191034,
+ starsurge = 78674,
+ sunfire = 93402,
+ stellarFlare = 202347,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ blessingOfElune = 202737,
+ blessingOfAnshe = 202739,
+ incarnationChoseOfElune = 102560,
+ celestialAlignment = 194223,
+ eclipse_lunar = 48518,
+ eclipse_solar = 48517,
+ onethsOverconfidence = 209407,
+ onethsIntuition = 209406,
+ solarEmpowerment = 164545,
+ innervate = 29166,
+ lunarEmpowerment = 164547,
+ warriorOfElune = 202425,
+ balanceForm = 24858,
+ stellarDrift = 202461,
+ emeraldDreamcatcher = 208190,
+ owlkinFrenzy = 157228,
+ powerOfEluneTheMoonGoddness = 208284,
+ sephuzSecret = 208052,
+ astralAcceleration = 242232,
+ newMoonController = -2027671,
+ solstice = 343648,
+ starfall = 191034,
+ starLord = 279709, --backwards compatible
+ starlord = 279709,
+ livelySpirit = 279279,
+ arcanicPulsar = 287790,
+ primordialArcanicPulsar = 338668,
+ balanceOfAllThings = 339942,
+ balanceOfAllThingsNature = 339943, -- Nature Buff
+ balanceOfAllThingsArcane = 339946, -- Arcane Buff
+ timewornDreambinder = 339949,
+ kindredEmpowerment = 327022,
+ onethsClearVision = 338661,
+ ravenousFrenzy = 323546,
+
+ },
+ conduits = {
+ preciseAlignment = 340706,
+
+
+ },
+ covenants = {
+ loneEmpowerment = 338142,
+ ravenousFrenzy = 323546,
+ empowerBond = 326446,
+ },
+ debuffs = {
+ stellarFlare = 202347,
+ moonfire = 164812,
+ sunfire = 164815,
+ stellarEmpowerment = 197637,
+ },
+ glyphs = {
+ },
+ runeforges = {
+ balanceOfAllThings = 339942,
+ lycarasFleetingGlimpse = 340059,
+ primordialArcanicPulsar = 338668,
+ timewornDreambinder = 339949,
+
+ },
+ talents = {
+ aetherialKindling = 327541,
+ astralCommunion = 202359,
+ astralSmolder = 394058,
+ balanceOfAllThings = 394049,
+ bloomingInfusion = 429433,
+ bounteousBloom = 429215,
+ celestialAlignment = 194223,
+ cenariusMight = 455797,
+ controlOfTheDream = 434249,
+ convokeTheSpirits = 323764,
+ denizenOfTheDream = 394076,
+ dreamSurge = 433831,
+ durabilityOfNature = 429227,
+ earlySpring = 428937,
+ eclipse = 79577,
+ elunesGuidance = 393991,
+ expansiveness = 429399,
+ forceOfNature = 205636,
+ furyOfElune = 202770,
+ grovesInspiration = 429402,
+ harmonyOfTheGrove = 428731,
+ incarnationChosenOfElune = 394013,
+ lightOfTheSun = 202918,
+ lunarShrapnel = 393868,
+ naturesBalance = 202430,
+ naturesGrace = 393959,
+ newMoon = 274281,
+ orbitBreaker = 383197,
+ potentEnchantments = 429420,
+ powerOfGoldrinn = 394046,
+ powerOfNature = 428859,
+ powerOfTheDream = 434220,
+ protectiveGrowth = 433748,
+ radiantMoonlight = 394121,
+ rattleTheStars = 393954,
+ shootingStars = 202342,
+ solarBeam = 78675,
+ solstice = 343647,
+ soulOfTheForest = 114107,
+ starfall = 191034,
+ starlord = 202345,
+ starweaver = 393940,
+ stellarFlare = 202347,
+ sunderedFirmament = 294108,
+ treantsOfTheMoon = 428544,
+ twinMoons = 279620,
+ umbralEmbrace = 393760,
+ umbralIntensity = 383195,
+ waningTwilight = 393956,
+ warriorOfElune = 202425,
+ wildMushroom = 88747,
+ },
+ traits = {
+ arcanicPulsar = 287773,
+ dawningSun = 276152,
+ highNoon = 278505,
+ lunarShrapnel = 278507,
+ powerOfTheMoon = 273367,
+ streakingStars = 272871,
+ sunblaze = 274397,
+ }
+ },
+ -- Feral
+ [103] = {
+ abilities = {
+ adaptiveSwarmDamage = 391889, -- Not Castable but needed for In-Flight
+ adaptiveSwarmHeal = 391891, -- Not Castable but needed for In-Flight
+ moonfireCat = 155625,
+ },
+ buffs = {
+ adaptiveSwarmDamage = 391889,
+ adaptiveSwarmHeal = 391891,
+ apexPredator = 255984,
+ apexPredatorsCraving = 391882, --339140,
+ berserk = 106951,
+ bloodtalons = 145152,
+ clearcasting = 135700,
+ elunesGuidance = 202060,
+ fieryRedMaimers = 236757,
+ incarnationAvatarOfAshamane = 102543,
+ ironJaws = 276021,
+ jungleStalker = 252071,
+ overflowingPower = 405189,
+ predatorRevealed = 408468,
+ predatorySwiftness = 69369,
+ ravage = 441585,
+ sabertooth = 391722,
+ savageRoar = 52610,
+ scentOfBlood = 285646,
+ smolderingFrenzy = 422751,
+ stampedingRoar = 77764,
+ suddenAmbush = 391974,
+ survivalInstincts = 61336,
+ tigersFury = 5217,
+ },
+ debuffs = {
+ adaptiveSwarmDamage = 391889,
+ adaptiveSwarmHeal = 391891,
+ bloodseekerVines = 439531,
+ -- direFixation = 417713,
+ feralFrenzy = 274838,
+ moonfireCat = 155625,
+ primalWrath = 1079,
+ rakeStun = 163505,
+ thrashCat = 405233,
+ },
+ items = {
+ algetharPuzzleBox = 193701,
+ ashesOfTheEmbersoul = 207167,
+ augmentation = 201325, -- Generic, place item id of desired augmentation
+ bandolierOfTwistedBlades = 207165,
+ fyrakksTaintedRageheart = 207164,
+ manicGrieftorch = 194308,
+ mirrorOfFracturedTomorrows = 207581,
+ mydasTalisman = 158319,
+ potion = 191383, -- Generic, place item id of desired potion
+ witherbarksBranch = 109999,
+ },
+ talents = {
+ adaptiveSwarm = 391888,
+ -- aggravateWounds = 441829,
+ apexPredatorsCraving = 391881,
+ ashamanesGuidance = 391548,
+ berserk = 106951,
+ berserkFrenzy = 384668,
+ berserkHeartOfTheLion = 391174,
+ -- bestialStrength = 441841,
+ bloodtalons = 319439,
+ -- bondWithNature = 439929,
+ brutalSlash = 202028,
+ carnivorousInstinct = 390902,
+ circleOfLifeAndDeath = 400320,
+ -- clawRampage = 441835,
+ coiledToSpring = 449537,
+ convokeTheSpirits = 391528,
+ doubleclawedRake = 391700,
+ dreadfulBleeding = 391045,
+ -- dreadfulWound = 441809,
+ -- empoweredShapeshifting = 441689,
+ -- entanglingVortex = 439895,
+ feralFrenzy = 274837,
+ -- flowerWalk = 439901,
+ -- fountOfStrength = 441675,
+ franticMomentum = 391875,
+ -- harmoniousConstitution = 440116,
+ -- huntBeneathTheOpenSkies = 439868,
+ -- implant = 440118,
+ incarnationAvatarOfAshamane = 102543,
+ infectedWounds = 48484,
+ -- killingStrikes = 441824,
+ -- lethalPreservation = 455461,
+ lionsStrength = 391972,
+ lunarInspiration = 155580,
+ mercilessClaws = 231063,
+ momentOfClarity = 236068,
+ omenOfClarity = 16864,
+ -- packsEndurance = 441844,
+ pouncingStrikes = 390772,
+ predator = 202021,
+ predatorySwiftness = 16974,
+ primalWrath = 285381,
+ ragingFury = 391078,
+ rampantFerocity = 391709,
+ -- ravage = 441583,
+ ripAndTear = 391347,
+ -- ruthlessAggression = 441814,
+ saberJaws = 421432,
+ sabertooth = 202031,
+ savageFury = 449645,
+ soulOfTheForest = 158476,
+ -- strategicInfusion = 439890,
+ -- strikeForTheHeart = 441845,
+ suddenAmbush = 384667,
+ survivalInstincts = 61336,
+ tasteForBlood = 384665,
+ -- tearDownTheMighty = 441846,
+ thrashingClaws = 405300,
+ -- thrivingGrowth = 439528,
+ tigersFury = 5217,
+ tigersTenacity = 391872,
+ tirelessEnergy = 383352,
+ -- twinSprouts = 440117,
+ unbridledSwarm = 391951,
+ veinripper = 391978,
+ -- vigorousCreepers = 440119,
+ wildSlashes = 390864,
+ -- wildpowerSurge = 441691,
+ -- wildshapeMastery = 441678,
+ -- wildstalkersPower = 439926,
+ },
+ },
+ -- Guardian
+ [104] = {
+ abilities = {
+ berserk = 50334,
+ incapacitatingRoar = 99,
+ incarnationGuardianOfUrsoc = 102558,
+ maul = 6807,
+ removeCorruption = 2782,
+ skullBash = 106839,
+ stampedingRoar = 106898,
+ survivalInstincts = 61336,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ berserk = 50334,
+ galacticGuardian = 213708,
+ goryFur = 201671,
+ incarnationGuardianOfUrsoc = 102558,
+ gore = 93622,
+ pulverize = 158792,
+ savageCombatant = 340613,
+ survivalInstincts = 61336,
+ toothAndClaw = 135286,
+ },
+ conduits = {
+ savageCombatant = 340609,
+
+ },
+ covenants = {
+ loneProtection = 338018,
+ },
+ debuffs = {
+ moonfireGuardian = 164812,
+ toothAndClaw = 135601,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ afterTheWildfire = 371905,
+ aggravateWounds = 441829,
+ berserkPersistence = 377779,
+ berserkRavage = 343240,
+ berserkUncheckedAggression = 377623,
+ bestialStrength = 441841,
+ bloodFrenzy = 203962,
+ brambles = 203953,
+ bristlingFur = 155835,
+ circleOfLifeAndDeath = 391969,
+ clawRampage = 441835,
+ convokeTheSpirits = 323764,
+ dreadfulWound = 441809,
+ dreamOfCenarius = 372119,
+ earthwarden = 203974,
+ elunesFavored = 370586,
+ empoweredShapeshifting = 441689,
+ flashingClaws = 393427,
+ fountOfStrength = 441675,
+ -- frontOfThePack = 377835,
+ furyOfNature = 370695,
+ galacticGuardian = 203964,
+ gore = 210706,
+ goryFur = 200854,
+ guardianOfElune = 213680,
+ improvedSurvivalInstincts = 328767,
+ incarnationGuardianOfUrsoc = 102558,
+ infectedWounds = 345208,
+ innateResolve = 377811,
+ killingStrikes = 441824,
+ layeredMane = 279552,
+ mangle = 231064,
+ maul = 6807,
+ packsEndurance = 441844,
+ pulverize = 80313,
+ rageOfTheSleeper = 200851,
+ ravage = 441583,
+ reinforcedFur = 393618,
+ reinvigoration = 372945,
+ rendAndTear = 204053,
+ ruthlessAggression = 441814,
+ scintillatingMoonlight = 238049,
+ soulOfTheForest = 158477,
+ strikeForTheHeart = 441845,
+ survivalInstincts = 61336,
+ survivalOfTheFittest = 203965,
+ tearDownTheMighty = 441846,
+ toothAndClaw = 135288,
+ twinMoonfire = 372567,
+ untamedSavagery = 372943,
+ -- ursineAdept = 300346,
+ ursocsEndurance = 393611,
+ ursocsFury = 372505,
+ ursocsGuidance = 393414,
+ viciousCycle = 371999,
+ vulnerableFlesh = 372618,
+ wildpowerSurge = 441691,
+ wildshapeMastery = 441678,
+ },
+ traits = {
+ layeredMane = 279552,
+ twistedClaws = 275906,
+ },
+ },
+ -- Restoration
+ [105] = {
+ abilities = {
+ efflorescence = 145205,
+ innervate = 29166,
+ ironbark = 102342,
+ lifebloom = 33763,
+ naturesCure = 88423,
+ nourish = 50464,
+ revitalize = 212040,
+ wrath = 5176,
+ sunfire = 93402,
+ tranquility = 740,
+ typhoon = 132469,
+ yserasGift = 145108,
+ naturesSwiftness = 132158,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ abundance = 207640,
+ cenarionWard = 102352,
+ clearcasting = 16870,
+ incarnationTreeOfLife = 33891,
+ innervate = 29166,
+ groveTending = 279793,
+ rejuvenationGermination = 155777,
+ lifebloom = 33763,
+ eclipse_lunar = 48518,
+ eclipse_solar = 48517,
+ --lunarEmpowerment = 164547,
+ regrowth = 8936,
+ --solarEmpowerment = 164545,
+ soulOfTheForest = 114108,
+ cultivat = 200389,
+ tranquility = 157982,
+ springblossom = 207386,
+ symbolOfHope = 64901,
+ fullbloom = 290213,
+ heartOfTheWild = 108291,
+ },
+ conduits = {
+ loneMeditation = 338035,
+ },
+ debuffs = {
+ moonfire = 164812,
+ sunfire = 164815,
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+ theDarkTitansLesson = 338831,
+ verdantInfusion = 338829,
+
+ },
+ talents = {
+ abundance = 207383,
+ balanceAffinity = 197632,
+ bloomingInfusion = 429433,
+ bondWithNature = 439929,
+ bounteousBloom = 429215,
+ buddingLeaves = 392167,
+ burstingGrowth = 440120,
+ callOfTheElderDruid = 426784,
+ cenarionWard = 102351,
+ cenariusGuidance = 393371,
+ cenariusMight = 455797,
+ controlOfTheDream = 434249,
+ cultivation = 200390,
+ dreamSurge = 433831,
+ durabilityOfNature = 429227,
+ earlySpring = 428937,
+ entanglingVortex = 439895,
+ expansiveness = 429399,
+ feralAffinity = 197490,
+ flourish = 197721,
+ flowerWalk = 439901,
+ germination = 155675,
+ grovesInspiration = 429402,
+ guardianAffinity = 197491,
+ harmoniousConstitution = 440116,
+ harmonyOfTheGrove = 428731,
+ huntBeneathTheOpenSkies = 439868,
+ implant = 440118,
+ incarnationTreeOfLife = 33891,
+ innerPeace = 197073,
+ lethalPreservation = 455461,
+ nourish = 50464,
+ overgrowth = 203651,
+ photosynthesis = 274902,
+ potentEnchantments = 429420,
+ powerOfNature = 428859,
+ powerOfTheDream = 434220,
+ protectiveGrowth = 433748,
+ resilientFlourishing = 439880,
+ rootNetwork = 439882,
+ soulOfTheForest = 158478,
+ springBlossoms = 207385,
+ strategicInfusion = 439890,
+ thrivingGrowth = 439528,
+ treantsOfTheMoon = 428544,
+ twinSprouts = 440117,
+ vigorousCreepers = 440119,
+ wildstalkersPower = 439926,
+ },
+ traits = {
+ dawningSun = 276152,
+ highNoon = 278505,
+ }
+ },
+ -- All
+ Shared = {
+ abilities = {
+ barkskin = 22812,
+ bearForm = 5487,
+ catForm = 768,
+ charmWoodlandCreature = 127757, -- Tome of the Wilds
+ cyclone = 33786,
+ dash = 1850,
+ dreamwalk = 193753,
+ entanglingRoots = 339,
+ ferociousBite = 22568,
+ flap = 164862,
+ flightForm = 165962,
+ growl = 6795,
+ mangle = 33917,
+ markOfTheWild = 1126,
+ moonfire = 8921,
+ mountForm = 210053,
+ prowl = 5215,
+ rebirth = 20484,
+ regrowth = 8936,
+ revive = 50769,
+ shred = 5221,
+ sunfire = 164815,
+ swipe = 213764,
+ swipeBear = 213771,
+ swipeCat = 106785,
+ teleportMoonglade = 18960,
+ thrashBear = 77758,
+ thrashCat = 106830,
+ trackBeasts = 210065,
+ trackHumanoids = 5225,
+ travelForm = 783,
+ wrath = 5176,
+ },
+ animas = {
+ lycarasBargin = 329960,
+ lycarasTwig = 330666,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ adaptiveSwarm = 325748,
+ barkskin = 22812,
+ bearForm = 5487,
+ burningEssence = 138927,
+ catForm = 768,
+ convokeTheSpirits = 391528,
+ dash = 1850,
+ tigerDash = 252216,
+ flightForm = 165962,
+ frenziedRegeneration = 22842,
+ heartOfTheWild = 319454,
+ ironfur = 192081,
+ kindredEmpowerment = 327139,
+ kindredSpiritsBuff = 326967, --This is buff on ourselves...at least as dps
+ kindredEmpowermentEnergize = 327022, --/kindred-empowerment this is when someone else cast it on you
+ loneSpirit = 338041,
+ lycarasTwig = 330668, -- Anima Power Torghast
+ markOfTheWild = 1126,
+ moonkinForm = 197625,
+ onethsPerception = 339800,
+ prowl = 5215,
+ rejuvenation = 774,
+ regrowth = 8936,
+ shadowmeld = 58984,
+ soulshape = 310143,
+ stagForm = 210053,
+ stampedingRoar = 106898,
+ stampedingRoarCat = 77764,
+ suddenAmbush = 340698,
+ travelForm = 783,
+ treantForm = 114282,
+ wildGrowth = 48438,
+ },
+ conduits = {
+ deepAllegiance = 341378,
+ tasteForBlood = 340682,
+ },
+ covenants = {
+ adaptiveSwarm = 325727,
+ adaptiveSwarmHeal = 325748,
+ convokeTheSpiritsCovenant = 323764,
+ kindredSpirits = 326434,
+ ravenousFrenzy = 323546,
+ },
+ debuffs = {
+ adaptiveSwarm = 325733,
+ cyclone = 33786,
+ entanglingRoots = 339,
+ growl = 6795,
+ hibernate = 2637,
+ lycarasBargin = 329961,
+ moonfire = 164812, --8921,
+ rake = 155722,
+ rip = 1079,
+ thrashBear = 192090,
+ thrashCat = 106830,
+ massEntanglement = 102359,
+ },
+ glyphs = {
+ -- glyphOfTheCheetah = 131113,
+ -- glyphOfTheDoe = 224122,
+ -- glyphOfTheFeralChameleon = 210333,
+ -- glyphOfTheOrca = 114333,
+ -- glyphOfTheSentinel = 219062,
+ -- glyphOfTheUrsolChameleon = 107059,
+ },
+ runeforges = {
+ apexPredatorsCraving = 339139,
+ eyeOfFearfullSymmetry = 339141,
+ frenzyband = 340053,
+ },
+ talents = {
+ aessinasRenewal = 474678,
+ astralInfluence = 197524,
+ circleOfTheHeavens = 474541,
+ circleOfTheWild = 474530,
+ cyclone = 33786,
+ felineSwiftness = 131768,
+ fluidForm = 449193,
+ forestwalk = 400129,
+ frenziedRegeneration = 22842,
+ galeWinds = 400142,
+ grievousWounds = 474526,
+ heartOfTheWild = 319454,
+ hibernate = 2637,
+ improvedBarkskin = 327993,
+ improvedStampedingRoar = 288826,
+ incapacitatingRoar = 99,
+ incessantTempest = 400140,
+ innervate = 29166,
+ instinctsOfTheClaw = 449184,
+ ironfur = 192081,
+ killerInstinct = 108299,
+ lingeringHealing = 231040,
+ loreOfTheGrove = 449185,
+ lycarasInspiration = 1232897,
+ lycarasTeachings = 378988,
+ maim = 22570,
+ massEntanglement = 102359,
+ mattedFur = 385786,
+ moonkinForm = 24858,
+ mightyBash = 5211,
+ naturalRecovery = 377796,
+ naturesVigil = 124974,
+ nurturingInstinct = 33873,
+ oakskin = 449191,
+ perfectlyhonedInstincts = 1213597,
+ primalFury = 159286,
+ rake = 1822,
+ rejuvenation = 774,
+ removeCorruption = 2782,
+ renewal = 108238,
+ rip = 1079,
+ skullBash = 106839,
+ soothe = 2908,
+ stampedingRoar = 106898,
+ starfire = 197628,
+ starlightConduit = 451211,
+ starsurge = 197626,
+ sunfire = 93402,
+ symbioticRelationship = 474750,
+ thickHide = 16931,
+ thrash = 106832,
+ tigerDash = 252216,
+ typhoon = 132469,
+ ursineVigor = 377842,
+ ursocsSpirit = 449182,
+ ursolsVortex = 102793,
+ verdantHeart = 301768,
+ wellhonedInstincts = 377847,
+ wildCharge = 102401,
+ wildGrowth = 48438,
+ },
+ talentsHeroic = {
+ -- bloomingInfusion = 429433,
+ -- bounteousBloom = 429215,
+ -- burstingGrowth = 440120,
+ -- cenariusMight = 455797,
+ -- controlOfTheDream = 434249,
+ -- dreamSurge = 433831,
+ -- durabilityOfNature = 429227,
+ -- earlySpring = 428937,
+ -- expansiveness = 429399,
+ -- grovesInspiration = 429402,
+ -- harmonyOfTheGrove = 428731,
+ -- potentEnchantments = 429420,
+ -- powerOfNature = 428859,
+ -- powerOfTheDream = 434220,
+ -- protectiveGrowth = 433748,
+ -- resilientFlourishing = 439880,
+ -- rootNetwork = 439882,
+ -- treantsOfTheMoon = 428544,
+ },
+ traits = {
+ livelySpirit = 279642,
+ },
+ },
+ },
+ EVOKER = {
+ -- Initial Evoker
+ [1465] = {
+ abilities = {
+ fireBreath = 357208,
+ },
+ },
+ -- Devastation
+ [1467] = {
+ abilities = {
+
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ burnout = 375802,
+ chargedBlast = 370454,
+ dragonrage = 375087,
+ emeraldTrance = 424155,
+ essenceBurst = 359618,
+ iridescenceBlue = 386399,
+ iridescenceRed = 386353,
+ leapingFlames = 370901,
+ powerSwell = 376850,
+ snapFire = 370818,
+ },
+ conduits = {
+
+ },
+ covenants = {
+
+ },
+ debuffs = {
+ shatteringStar = 370452,
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+
+ },
+ talents = {
+ animosity = 375797,
+ arcaneIntensity = 375618,
+ arcaneVigor = 386342,
+ azureEssenceBurst = 375721,
+ burnout = 375801,
+ catalyze = 386283,
+ causality = 375777,
+ chargedBlast = 370455,
+ denseEnergy = 370962,
+ dragonrage = 375087,
+ engulfingBlaze = 370837,
+ eternitySurge = 359073,
+ eternitysSpan = 375757,
+ eventHorizon = 411164,
+ everburningFlame = 370819,
+ eyeOfInfinity = 411165,
+ feedTheFlames = 369846,
+ firestorm = 368847,
+ focusingIris = 386336,
+ fontOfMagic = 411212,
+ heatWave = 375725,
+ hoardedPower = 375796,
+ honedAggression = 371038,
+ immenentDestruction = 370781,
+ imposingPresence = 371016,
+ innerRadiance = 386405,
+ iridescence = 370867,
+ layWaste = 371034,
+ onyxLegacy = 386348,
+ powerSwell = 370839,
+ pyre = 357211,
+ ragingInferno = 405659,
+ rubyEmbers = 365937,
+ rubyEssenceBurst = 376872,
+ scintillation = 370821,
+ shatteringStar = 370452,
+ snapFire = 370783,
+ spellweaversDominance = 370845,
+ titanicWrath = 386272,
+ tyranny = 376888,
+ volatility = 369089,
+ },
+ traits = {
+
+ },
+ },
+ -- Preservation
+ [1468] = {
+ abilities = {
+ echo = 364343,
+ naturalize = 360823,
+ reversion = 366155,
+ rewind = 363534,
+
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ echo = 364343,
+ essenceBurst = 369297,
+ reversion = 366155,
+ timeDilation = 357170,
+
+ },
+ conduits = {
+
+ },
+ covenants = {
+
+ },
+ debuffs = {
+
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+
+ },
+ talents = {
+ dreamBreath = 355936,
+ emeraldCommunion = 370960,
+ spiritbloom = 367226,
+ timeDilation = 357170,
+ temporalAnomaly = 373861,
+
+ },
+ traits = {
+
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ azureStrike = 362969,
+ blessingOfTheBronze = 364342,
+ choosenIdentity = 360022,
+ deepBreath = 357210,
+ disintegrate = 356995,
+ emeraldBlossom = 355913,
+ fireBreath = 357208,
+ furyOfTheAspects = 390386,
+ hover = 358267,
+ livingFlame = 361469,
+ -- quell = 351338,
+ -- renewingBlaze = 374348,
+ returnEvoker = 361227,
+ tailSwipe = 368970,
+ wingBuffet = 357214,
+ visage = 351239,
+ },
+ animas = {
+
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ ancientFlame = 375583,
+ blazingShards = 409848,
+ blessingOfTheBronze = 364342,
+ hover = 358267,
+ scarletAdaptation = 372470,
+ soar = 381322,
+ sourceOfMagic = 372581,
+ tipTheScales = 370553,
+ visage = 372014,
+ visageForm = 351239,
+ },
+ conduits = {
+
+ },
+ covenants = {
+
+ },
+ debuffs = {
+ fireBreath = 357209,
+ shatteringStar = 370452,
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+
+ },
+ talents = {
+ aerialMastery = 365933,
+ ancientFlame = 369990,
+ attunedToTheDream = 376930,
+ blastFurnace = 375510,
+ bountifulBloom = 370886,
+ cauterizingFlame = 374251,
+ clobberingSweep = 375443,
+ draconicLegacy = 376166,
+ enkindled = 375554,
+ essenceAttunement = 375722,
+ expunge = 365585,
+ extendedFlight = 375517,
+ exuberance = 375542,
+ fireWithin = 375577,
+ fociOfLife = 375574,
+ forgerOfMountains = 375528,
+ heavyWingbeats = 368838,
+ inherentResistance = 375544,
+ innateMagic = 375520,
+ instinctiveArcana = 376164,
+ landslide = 358385,
+ leapingFlames = 369939,
+ lushGrowth = 375561,
+ naturalConvergence = 369913,
+ obsidianBulwark = 375406,
+ obsidianScales = 363916,
+ oppressingRoar = 372048,
+ overawe = 374346,
+ panacea = 387761,
+ permeatingChill = 370897,
+ potentMana = 418101,
+ powerNexus = 369908,
+ protractedTalons = 369909,
+ quell = 351338,
+ recall = 371806,
+ regenerativeMagic = 387787,
+ renewingBlaze = 374348,
+ rescue = 370665,
+ scarletAdaptation = 372469,
+ sleepWalk = 360806,
+ sourceOfMagic = 369459,
+ tailwind = 375556,
+ terrorOfTheSkies = 371032,
+ timeSpiral = 374968,
+ tipTheScales = 370553,
+ twinGuardian = 370888,
+ unravel = 368432,
+ verdantEmbrace = 360995,
+ wallopingBlow = 387341,
+ zephyr = 374227,
+ },
+ traits = {
+
+ },
+ },
+ },
+ HUNTER = {
+ -- BeastMastery
+ [253] = {
+ abilities = {
+ aspectOfTheWild = 193530,
+ barbedShot = 217200,
+ bestialWrath = 19574,
+ cobraShot = 193455,
+ concussiveShot = 5116,
+ counterShot = 147362,
+ intimidation = 19577,
+ killCommand = 34026,
+ killShot = 53351,
+ multishot = 2643,
+ },
+ animas = {
+ piercingScope = 331183,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ aspectOfTheWild = 193530,
+ barbedShot = 217200,
+ beastCleave = 118455,
+ berserking = 26297,
+ bestialWrath = 19574,
+ danceOfDeath = 274443,
+ direBeast = 120694,
+ frenzy = 272790,
+ parselsTongue = 248084,
+ spittingCobra = 194407,
+ volley = 194386,
+ },
+ debuffs = {
+ barbedShot = 217200,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ animalCompanion = 267116,
+ barrage = 120360,
+ bloodshed = 321530,
+ direBeast = 120679,
+ killCleave = 378207,
+ killerCobra = 199532,
+ killerInstinct = 273887,
+ oneWithThePack = 199528,
+ scentOfBlood = 193532,
+ stampede = 201430,
+ stomp = 199530,
+ thrillOfTheHunt = 257944,
+ wailingArrow = 392060,
+ },
+ traits = {
+ danceOfDeath = 274441,
+ primalInstincts = 279806,
+ rapidReload = 278530,
+ },
+ },
+ -- Marksmanship
+ [254] = {
+ abilities = {
+ },
+ artifacts = {
+ },
+ buffs = {
+ bulletstorm = 389020,
+ bullseye = 204090,
+ deadEye = 321461,
+ deathblow = 378770,
+ feignDeath = 5384,
+ inTheRhythm = 272733,
+ lethalShots = 260395,
+ lockAndLoad = 194594,
+ masterMarksman = 269576,
+ preciseShots = 260242,
+ razorFragments = 388993,
+ salvo = 400456,
+ steadyFocus = 193534,
+ trickShots = 257622,
+ trueshot = 288613,
+ unerringVision = 274447,
+ },
+ debuffs = {
+ huntersMark = 257284,
+ serpentSting = 271788,
+ },
+ glyphs = {
+
+ },
+ interrupts = {
+ counterShot = 147362,
+ },
+ talents = {
+ aimedShot = 19434,
+ barrage = 120360,
+ bindingShackles = 321468,
+ bulletstorm = 389019,
+ bullseye = 204089,
+ callingTheShots = 260404,
+ carefulAim = 260228,
+ chimaeraShot = 342049,
+ crackShot = 321293,
+ deathblow = 378769,
+ eagletalonsTrueFocus = 389449,
+ explosiveShot = 212431,
+ fanTheHammer = 459794,
+ focusedAim = 378767,
+ heavyAmmo = 378910,
+ hydrasBite = 260241,
+ improvedSteadyShot = 321018,
+ inTheRhythm = 407404,
+ killShot = 53351,
+ killZone = 459921,
+ killerAccuracy = 378765,
+ legacyOfTheWindrunners = 406425,
+ lightAmmo = 378913,
+ lockAndLoad = 194595,
+ loneWolf = 155228,
+ masterMarksman = 260309,
+ multishot = 257620,
+ nightHunter = 378766,
+ penetratingShots = 459783,
+ preciseShots = 260240,
+ rapidFire = 257044,
+ rapidFireBarrage = 459800,
+ razorFragments = 384790,
+ readiness = 389865,
+ salvo = 400456,
+ serpentstalkersTrickery = 378888,
+ smallGameHunter = 459802,
+ steadyFocus = 193533,
+ streamline = 260367,
+ surgingShots = 391559,
+ tacticalReload = 400472,
+ trickShots = 257621,
+ trueshot = 288613,
+ unerringVision = 386878,
+ volley = 260243,
+ wailingArrow = 459806,
+ },
+ traits = {
+ focusedFire = 278531,
+ inTheRhythm = 264198,
+ rapidReload = 278530,
+ steadyAim = 277651,
+ surgingShots = 287707,
+ unerringVision = 274444,
+ },
+ },
+ -- Survival
+ [255] = {
+ abilities = {
+ aspectOfTheEagle = 186289,
+ carve = 187708,
+ coordinatedAssault = 266779,
+ harpoon = 190925,
+ intimidation = 19577,
+ killCommand = 259489,
+ killShot = 320976,
+ muzzle = 187707,
+ pheromoneBomb = 270323, -- Wildfire Infusion
+ raptorStrike = 186270,
+ serpentSting = 259491,
+ shrapnelBomb = 270335, -- Wildfire Infusion
+ volatileBomb = 271045, -- Wildfire Infusion
+ wakeUp = 210000,
+ wildfireBomb = 259495,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ aspectOfTheEagle = 186289,
+ blurOfTalons = 277969,
+ coordinatedAssault = 360952,
+ exposedFlank = 252094, -- Tier 21
+ mongooseFury = 259388,
+ tipOfTheSpear = 260286,
+ vipersVenom = 268552,
+ madBombardier = 363805, -- Tier 28
+ },
+ debuffs = {
+ bloodseeker = 259277,
+ internalBleeding = 270343,
+ serpentSting = 259491,
+ shrapnelBomb = 270339,
+ wildfireBomb = 269747,
+ pheromoneBomb = 270332,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ alphaPredator = 269737,
+ birdsOfPrey = 260331,
+ bloodseeker = 260248,
+ butchery = 212436,
+ crackShot = 321293,
+ flankingStrike = 269751,
+ furyOfTheEagle = 203415,
+ guerrillaTactics = 264332,
+ hydrasBite = 260241,
+ mongooseBite = 259387,
+ steelTrap = 162488,
+ termsOfEngagement = 265895,
+ tipOfTheSpear = 260285,
+ vipersVenom = 268501,
+ wildfireInfusion = 271014,
+ },
+ traits = {
+ blurOfTalons = 277653,
+ latentPoison = 273283,
+ primevalIntuition = 288570,
+ upCloseAndPersonal = 278533,
+ venomousFangs = 274590,
+ wildernessSurvival = 279589,
+ }
+ },
+ -- All
+ Shared = {
+ abilities = {
+ -- Hunter Abilities
+ arcaneShot = 185358,
+ aspectOfTheChameleon = 61648,
+ aspectOfTheCheetah = 186257,
+ aspectOfTheTurtle = 186265,
+ disengage = 781,
+ eagleEye = 6197,
+ exhilaration = 109304,
+ eyeOfTheBeast = 321297,
+ feignDeath = 5384,
+ flare = 1543,
+ fortitudeOfTheBear = 392956,
+ freezingTrap = 187650,
+ huntersMark = 257284,
+ steadyShot = 56641,
+ trackBeasts = 1494,
+ trackDemons = 19878,
+ trackDragonkin = 19879,
+ trackElementals = 19880,
+ trackGiants = 19882,
+ trackHidden = 19885,
+ trackHumanoids = 19883,
+ trackMechanicals = 229533,
+ trackUndead = 19884,
+ wingClip = 195645,
+ -- Pet Management
+ beastLore = 1462,
+ callPet = 9,
+ callPet1 = 883,
+ callPet2 = 83242,
+ callPet3 = 83243,
+ callPet4 = 83244,
+ callPet5 = 83245,
+ commandPet = 272651,
+ dismissPet = 2641,
+ feedPet = 6991,
+ fetch = 125050,
+ mendPet = 136,
+ playDead = 209997,
+ revivePet = 982,
+ tameBeast = 1515,
+ wakeUp = 210000,
+ ----------------------------
+ -- Pet - Basic Abilities ---
+ ----------------------------
+ bite = 17253, -- Basalisk, Bat, Beetle, Blood Beast, Boar, Carapid, Carrion Bird, Chimera, Core Hound, Crocolisk, Devilsaur, Direhorn, Dragonhawk, Feathermane, Fox, Hound, Hydra, Hyena, Lizard, Mechanical, Pterrordax, Ravager, Ray, Riverbeast, Serpent, Shale Beast, Spider, Stone Hound, Turtle, Warp Stalker, Water Strider, Wind Serpent, Wolf, Worm
+ claw = 16827, -- Aqiri, Bear, Bird of Prey, Cat, Crab, Raptor, Rodent, Scorpid, Spirit Beast, Tallstrider
+ dash = 61684, -- Speed Increase (ALL PETS)
+ smack = 49966, -- Camel, Clefthoof, Courser, Crane, Gorilla, Gruffhorn, Mammoth, Monkey, Moth, Oxen, Scalehide, Sporebat, Stag, Toad, Wasp
+ growl = 2649, -- Taunt (ALL PETS)
+ ---------------------------
+ -- Pet - Spec Abilities ---
+ ---------------------------
+ primalRage = 264667, -- Ferocity Dmg Cd
+ -- survivalOfTheFittest = 264735, -- Tenacity Def Cd
+ mastersCall = 53271, -- Cunning Freedom
+ ------------------------------
+ -- Pet - Special Abilities ---
+ ------------------------------
+ -- AOE
+ burrowAttack = 93433, -- Worm
+ froststormBreath = 92380, -- Chimera - Channeled Cone AOE
+ -- Defense/Dodge
+ agileReflexes = 160011, -- Fox - Dodge
+ bristle = 263869, -- Boar - Defensive
+ bulwark = 279410, -- Carapid - Defensive
+ catlikeReflexes = 263892, -- Cat - Dodge
+ defenseMatrix = 263868, -- Mechanical - Defensive
+ dragonsGuile = 263887, -- Dragonhawk - Dodge
+ featherFlurry = 263916, -- Feathermane - Dodge
+ fleethoof = 341117, -- Courser - Dodge
+ hardenCarapace = 90339, -- Beetle - Defensive
+ obsidianSkin = 263867, -- Core Hound - Defensive
+ primalAgility = 160044, -- Monkey - Dodge
+ scaleShield = 263865, -- Scalehide - Defensive
+ serpentSwiftness = 263904, -- Serpent - Dodge
+ shellShield = 26064, -- Turtle - Defensive
+ solidShell = 160063, -- Shale Beast - Defensive
+ swarmOfFlies = 279336, -- Toad - Dodge
+ wingedAgility = 264360, -- Wind Serpent - Dodge
+ -- Dispel
+ chiJiTranq = 344350, -- Crane
+ naturesGrace = 344352, -- Stag
+ netherEnergy = 344349, -- Ray -- netherEnergy
+ serenityDust = 344353, -- Moth
+ sonicScreech = 344348, -- Bat -- sonicScreech
+ soothingWater = 344346, -- Water Strider
+ spiritPulse = 344351, -- Spirit Beast -- spiritPulse
+ sporeCloud = 344347, -- Sporebat
+ -- Heal
+ eternalGuardian = 267922, -- Stone Hound - Ressurect
+ feast = 159953, -- Devilsaur - Heal (req dead humanoid/beast nearby)
+ spiritmend = 90361, -- Spirit Beast - Heal
+ -- Mortal Wounds Debuff
+ acidBite = 263863, -- Hydra - Mortal Wounds Debuff
+ bloodyScreech = 24423, -- Carrion Bird - Mortal Wounds Debuff
+ deadlySting = 160060, -- Scorpid - Mortal Wounds Debuff
+ gnaw = 263856, -- Rodent - Mortal Wounds Debuff
+ gore = 263861, -- Direhorn - Mortal Wounds Debuff
+ grievousBite = 279362, -- Lizard - Mortal Wounds Debuff
+ gruesomeBite = 160018, -- Riverbeast - Mortal Wounds Debuff
+ infectedBite = 263853, -- Hyena - Mortal Wounds Debuff
+ monsterousBite = 54680, -- Devilsaur - Mortal Wounds Debuff
+ ravage = 263857, -- Ravager - Mortal Wounds Debuff
+ savageRend = 263854, -- Savage Rend - Mortal Wounds Debuff
+ toxicSting = 263858, -- Wasp - Mortal Wounds Debuff
+ -- Slow
+ acidSpit = 263446, -- Worm - Slow
+ ankleCrack = 50433, -- Crocolisk - Slow
+ bloodBolt = 288962, -- Blood Beast - Slow
+ dustCloud = 50285, -- Tallstrider - Slow
+ frostBreath = 54644, -- Chimera - Slow
+ furiousBite = 263840, -- Wolf - Slow
+ lockJaw = 263423, -- Hound - Slow
+ petrifyingGaze = 263841, -- Basilisk - Slow
+ pin = 50245, -- Crab - Slow
+ talonRend = 263852, -- Bird of Prey - Slow
+ tendonRip = 160065, -- Aqiri - Slow
+ trample = 341118, -- Mammoth - Slow
+ warpTime = 35346, -- Warp Stalker - Slow
+ webSpray = 160067, -- Spider - Slow
+ -- Slow Fall
+ updraft = 160007, -- Feathermane/Pterrordax - Slow Fall
+ -- Stealth
+ prowl = 24450, -- Stealth
+ spiritWalk = 90328, -- Spirit Beast - Stealth
+ -- Tricks / Play / Rest
+ play = 90347, -- Fox
+ restBear = 94019, -- Bear
+ restRodent = 126364, -- Rodent
+ trickCrane = 126259, -- Crane
+ trickBirdOfPrey = 94022, -- Bird of Prey
+ -- Water Walking
+ surfaceTrot = 126311, -- Water Strider - Water Walking
+ },
+ animas = {
+ soulforgeEmbers = 331197,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ aspectOfTheTurtle = 186265,
+ eagletalonsTrueFocus = 336851,
+ feignDeath = 5384,
+ flayersMark = 324156, -- Covenant
+ killingFrenzy = 363665, -- T28 4pc
+ mendPet = 136,
+ nesingwarysTrappingApparatus = 336744,
+ playDead = 209997,
+ prowl = 24450,
+ resonatingArrow = 356263, -- Covenant
+ spiritWalk = 90328,
+ surfaceTrot = 126311, -- Water Strider - Water Walking
+ updraft = 160007, -- Feathermane/Pterrordax - Slow Fall
+ volley = 260243,
+ wildSpirits = 328231,
+ },
+ conduits = {
+ bloodletting = 341440,
+ marksmansAdvantage = 339264,
+ },
+ covenants = {
+ flayedShot = 324149,
+ resonatingArrow = 308491,
+ wildSpirits = 328231,
+ },
+ debuffs = {
+ bestialFerocity = 191413,
+ freezingTrap = 3355,
+ huntersMark = 257284,
+ intimidation = 24934,
+ latentPoison = 378015,
+ mortalWounds = 115804, -- Pet Ability Debuff
+ resonatingArrow = 308498, -- Covenant
+ soulforgeEmbers = 331269, -- Covenant
+ tarTrap = 135299,
+ wildMark = 328275, -- Covenant
+ },
+ glyphs = {
+
+ },
+ interrupts = {
+ freezingTrap = 187650,
+ },
+ runeforges = {
+ bagOfMunitions = 356264,
+ eagletalonsTrueFocus = 336849,
+ nesingwarysTrappingApparatus = 336743,
+ pouchOfRazorFragments = 356618,
+ rylakstalkersConfoundingStrikes = 336901,
+ secretsOfTheUnblinkingVigil = 336878,
+ serpentstalkersTrickery = 336870,
+ soulforgeEmbers = 336745,
+ surgingShots = 336867,
+ qaplaEredunWarOrder = 336830,
+ },
+ talents = {
+ bindingShot = 109248,
+ blackrockMunitions = 462036,
+ bornToBeWild = 266921,
+ burstingShot = 186387,
+ camouflage = 199483,
+ concussiveShot = 5116,
+ counterShot = 147362,
+ devilsaurTranquilizer = 459991,
+ disruptiveRounds = 343244,
+ emergencySalve = 459517,
+ entrapment = 393344,
+ ghillieSuit = 459466,
+ highExplosiveTrap = 236776,
+ huntersAvoidance = 384799,
+ implosiveTrap = 462031,
+ improvedKillShot = 343248,
+ improvedTraps = 343247,
+ intimidation = 19577,
+ keenEyesight = 378004,
+ kindlingFlare = 459506,
+ kodoTranquilizer = 459983,
+ loneSurvivor = 388039,
+ misdirection = 34477,
+ momentOfOpportunity = 459488,
+ naturalMending = 270581,
+ noHardFeelings = 459546,
+ paddedArmor = 459450,
+ pathfinding = 378002,
+ posthaste = 109215,
+ quickLoad = 378771,
+ rejuvenatingWind = 385539,
+ roarOfSacrifice = 53480,
+ scareBeast = 1513,
+ scatterShot = 213691,
+ scoutsInstincts = 459455,
+ scrappy = 459533,
+ serratedTips = 459502,
+ survivalOfTheFittest = 264735,
+ specializedArsenal = 459542,
+ tarTrap = 187698,
+ tarcoatedBindings = 459460,
+ territorialInstincts = 459507,
+ trailblazer = 199921,
+ tranquilizingShot = 19801,
+ triggerFinger = 459534,
+ unnaturalCauses = 459527,
+ wildernessMedicine = 343242,
+ },
+ talentsHeroic = {
+ -- Dark Ranger
+ blackArrow = 430703,
+ darkChains = 430712,
+ darkEmpowerment = 430718,
+ deathShade = 430711,
+ darknessCalls = 430722,
+ embraceTheShadows = 430704,
+ graveReaper = 430719,
+ overshadow = 430716,
+ shadowErasure = 430720,
+ shadowHounds = 430707,
+ shadowLash = 430717,
+ shadowSurge = 430714,
+ smokeScreen = 430709,
+ witheringFire = 430715,
+ -- Pack Leader
+ beastOfOpportunity = 445700,
+ corneredPrey = 445702,
+ coveringFire = 445715,
+ cullTheHerd = 445717,
+ denRecovery = 445710,
+ frenziedTear = 445696,
+ furiousAssault = 445699,
+ howlOfThePack = 445707,
+ packAssault = 445721,
+ packCoordination = 445505,
+ scatteredPrey = 445768,
+ tirelessHunt = 445701,
+ viciousHunt = 445404,
+ wildAttacks = 445708,
+ -- Sentinel
+ catchOut = 451516,
+ crescentSteel = 451530,
+ extrapolatedShots = 450374,
+ eyesClosed = 450381,
+ dontLookBack = 450373,
+ invigoratingPulse = 450379,
+ lunarStorm = 450385,
+ overwatch = 450384,
+ releaseAndReload = 450376,
+ sentinel = 450369,
+ sentinelPrecision = 450375,
+ sentinelWatch = 451546,
+ sideline = 450378,
+ symphonicArsenal = 450383,
+ },
+ },
+ },
+ MAGE = {
+ -- Arcane
+ [62] = {
+ abilities = {
+ alterTime = 342245,
+ arcaneBarrage = 44425,
+ arcaneBlast = 30451,
+ arcaneFamiliar = 205022,
+ arcaneMissiles = 5143,
+ arcaneOrb = 153626,
+ arcanePower = 12042,
+ chargedUp = 205032,
+ conjuremanaGem = 759,
+ displacement = 212801,
+ erosion = 205039,
+ evocation = 12051,
+ -- fireblast = 519836,
+ greaterInvisibility = 110959,
+ markOfAluneth = 224968,
+ netherTempest = 114923,
+ presenceofMind = 205025,
+ prismaticBarrier = 235450,
+ runeofPower = 116011,
+ slow = 31589,
+ supernova = 157980,
+ touchOfTheMagi = 321507,
+ },
+ artifacts = {
+ aegwynnsAscendance = 187680,
+ aegwynnsFury = 187287,
+ aegwynnsInperative = 187264,
+ aegwynnsIntensity = 238054,
+ aegwynnsWrath = 187321,
+ alunethsAvarice = 238090,
+ ancientPower = 214917,
+ arcanePurification = 187313,
+ arcaneRebound = 188006,
+ blastingRod = 187258,
+ cracklingEnergy = 187310,
+ etherealSensitivity = 187276,
+ everywhereAtOnce = 187301,
+ forceBarrier = 210716,
+ intensityOfTheTirisgarde = 241121,
+ markOfAluneth = 224968,
+ mightOfTheGuardians = 187318,
+ ruleOfThrees = 215463,
+ sloooowDown = 210730,
+ timeAndSpace = 238126,
+ torrentialBarrage = 187304,
+ touchOfTheMagi = 210725,
+ },
+ buffs = {
+ arcaneCharge = 36032,
+ arcaneFamiliar = 210126,
+ arcanePower = 12042,
+ -- arcaneCharge = 36032,
+ evocation = 12051,
+ expandedPotential = 327495,
+ arcaneMissles = 79683,
+ presenceOfMind = 205025,
+ prismaticBarrier = 235450,
+ rhoninsAssaultingArmwraps = 208081,
+ runeofPower = 116011,
+ ruleOfThrees = 264774,
+ brainStorm = 273330,
+ clearcasting = 263725,
+ },
+ debuffs = {
+ arcaneCharge = 36032,
+ netherTempest = 114923,
+ touchoftheMagi = 210824,
+
+
+ },
+ glyphs = {
+
+ },
+ talents = {
+ arcaneEcho = 342231,
+ amplification = 236628,
+ arcaneFamiliar = 205022,
+ arcaneOrb = 153626,
+ chronoShift = 235711,
+ mastersOfTime = 342249,
+ --erosion = 205039,
+ netherTempest = 114923,
+ overpowered = 155147,
+ resonance = 205028,
+ reverberate = 281482,
+ rulesOfThrees = 264354,
+ slipstream = 236457,
+ supernova = 157980,
+ timeAnomaly = 210805
+ --temporalFlux = 234302,
+ --wordsOfPower = 205035,
+ },
+ traits = {
+ anomalousImpact = 279867,
+ arcanePressure = 274594,
+ arcanePummeling = 270669,
+ brainStorm = 273326,
+ explosiveEcho = 278537,
+ galvanizingSpark = 278536,
+ },
+ },
+ -- Fire
+ [63] = {
+ abilities = {
+ alterTime = 108978,
+ blazingBarrier = 235313,
+ blastWave = 157981,
+ cinderstorm = 198929,
+ combustion = 190319,
+ dragonsBreath = 31661,
+ disciplinaryCommand = 327365,
+ fireball = 133,
+ fireBlast = 108853,
+ fireBlast2 = 319836,
+ flameOn = 205029,
+ flamestrike = 2120,
+ infernalCascade = 336821,
+ livingBomb = 44457,
+ meteor = 153561,
+ phoenixFlames = 257541,
+ pyroblast = 11366,
+ shiftingPower = 314791,
+ scorch = 2948,
+ soulIgnition = 345211,
+ },
+ artifacts = {
+ aftershocks = 194431,
+ phoenixReborn = 215773,
+ },
+ buffs = {
+ blazingBarrier = 235313,
+ blasterMaster = 274598,
+ combustion = 190319,
+ disciplinaryArcane = 327369,
+ disciplinaryCommand = 327365,
+ disciplinaryCommandFrost = 327366,
+ disciplinaryCommandFire = 327368,
+ firestorm = 333097,
+ heatingUp = 48107,
+ hotStreak = 48108,
+ iceFloes = 108839,
+ infernalCascade = 336832,
+ kaelthasUltimateAbility = 209455,
+ preheat = 273333,
+ pyroclasm = 269651,
+ shiftingPower = 314791,
+ sunKingsBlessings = 333313,
+ soulIgnition = 345211,
+ },
+ debuffs = {
+ ignite = 12654,
+ meteorBurn = 155158,
+ cauterized = 87024,
+
+ },
+ glyphs = {
+
+ },
+ talents = {
+ alexstraszasFury = 235870,
+ blastWave = 157981,
+ blazingSoul = 235365,
+ --cinderstorm = 198929,
+ conflagration = 205023,
+ --controlledBurn = 205033,
+ freneticSpeed = 236058,
+ firestarter = 205026,
+ flameOn = 205029,
+ flamePatch = 205037,
+ kindling = 155148,
+ livingBomb = 44457,
+ meteor = 153561,
+ focusMagic = 321358,
+ fromTheAshes = 342344,
+ pyromaniac = 205020,
+ pyroclasm = 269650,
+ searingTouch = 269644,
+ },
+ covenants = {
+ shiftingPower = 314791,
+ },
+ traits = {
+ preheat = 273332,
+ blasterMaster = 274596,
+ },
+ conduits = {
+ infernalCascade = 336821,
+ },
+ runeforges = {
+ firestorm = 333097,
+ grislyIcicle = 333393,
+ disciplinaryCommand = 327365,
+ sunKingsBlessings = 333313
+ }
+ },
+ -- Frost
+ [64] = {
+ abilities = {
+ alterTime = 108978,
+ blizzard = 190356,
+ coldSnap = 235219,
+ coneOfCold = 120,
+ --fireBlast = 108853, - Fire Mage version, Frost should not have this and already listed fireBlast below
+ flurry = 44614,
+ freeze = 231596,
+ frostbolt = 116,
+ frostBomb = 112948,
+ frozenOrb = 84714,
+ frozenTouch = 205030,
+ fireBlast = 319836,
+ iceBarrier = 11426,
+ iceFloes = 108839,
+ iceForm = 198144,
+ iceLance = 30455,
+ iceNova = 157997,
+ icyVeins = 12472,
+ petFreeze = 33395,
+ rayOfFrost = 205021,
+ runeofPower = 116011,
+ removeCurse = 475,
+ summonWaterElemental = 31687,
+ waterbolt = 31707,
+ },
+ artifacts = {
+ -- icyHand = 220817,
+ },
+ buffs = {
+ brainFreeze = 190446,
+ concentratedCoolness = 198148,
+ freezingRain = 270232,
+ frostBomb = 112948,
+ fingersOfFrost = 44544,
+ icyVeins = 12472,
+ chainReaction = 195418,
+ zannesuJourney = 226852,
+ timeWarp = 80353,
+ iceFloes = 108839,
+ iceForm = 198144,
+ iceBarrier = 11426,
+ icicles = 205473,
+ frozenMass = 242253,
+ },
+ debuffs = {
+ chainsOfIce = 65173,
+ chilled = 205708,
+ frostBomb = 112948,
+ frostNova = 122,
+ iceNova = 157997,
+ wintersChill = 228358,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ --articGale = 205038,
+ boneChilling = 205027,
+ cometStorm = 153595,
+ chainReaction = 278309,
+ ebonbolt = 257537,
+ --frostBomb = 112948,
+ frozenTouch = 205030,
+ frigidWinds = 235224,
+ freezingRain = 270233,
+ focusMagic = 321358,
+ glacialSpike = 199786,
+ glacialInsulation = 235297,
+ iceNova = 157997,
+ iceFloes = 108839,
+ lonelyWinter = 205024,
+ rayOfFrost = 205021,
+ splittingIce = 56377,
+ thermalVoid = 155149,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ fireBlast = 319836,
+ frostbolt = 116,
+ arcaneIntellect = 1459,
+ arcaneExplosion = 1449,
+ blink = 1953,
+ counterspell = 2139,
+ conjureRefreshment = 190336,
+ -- fireBlast = 319836,
+ frostBolt = 116,
+ frostNova = 122,
+ iceBlock = 45438,
+ invisibility = 66,
+ mirrorImage = 55342,
+ polymorph = 118,
+ removeCurse = 475,
+ ringOfFrost = 113724,
+ runeOfPower = 116011,
+ --shimmer = 212653,
+ slowFall = 130,
+ spellsteal = 30449,
+ teleportExodar = 32271,
+ timeWarp = 80353,
+ waterJet = 135029,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ arcaneIntellect = 1459,
+ disciplinaryCommand = 327365,
+ expandedPotential = 327489,
+ focusMagic = 321358,
+ freezingWinds = 327364,
+ iceBlock = 45438,
+ incantersFlow = 1463,
+ slowFall = 130,
+ slickIce = 327509,
+ runeOfPower = 116014,
+ },
+ conduits = {
+
+ },
+ covenants = {
+ deathborne = 324220,
+ mirrorsOfTorment = 314793,
+ radiantSpark = 307443,
+ shiftingPower = 314791,
+ },
+ debuffs = {
+ frostNova = 122,
+ mirrorsOfTorment = 314793,
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+ disciplinaryCommand = 327365,
+ sunKingsBlessings = 333313,
+ grislyIcicle = 333393,
+ firestorm = 333097,
+
+ },
+ talents = {
+ incantersFlow = 1463,
+ iceWard = 205036,
+ focusMagic = 321358,
+ ringOfFrost = 113724,
+ runeOfPower = 116011,
+ shimmer = 212653,
+ --unstableMagic = 157976,
+ },
+ },
+ },
+ MONK = {
+ -- Brewmaster
+ [268] = {
+ abilities = {
+ blackoutKick = 205523,
+ breathOfFire = 115181,
+ celestialBrew = 322507,
+ clash = 324312,
+ invokeNiuzao = 132578,
+ kegSmash = 121253,
+ purifyingBrew = 119582,
+ spinningCraneKick = 322729,
+ zenMeditation = 115176
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ eyeOfTheTiger = 196608,
+ ironskinBrew = 215479,
+ blackoutCombo = 228563,
+ purifiedChi = 325092,
+ rushingJadeWind = 116847,
+ zenMeditation = 115176,
+ celestialBrew = 322507,
+ invokeNiuzao = 132578,
+ counterStrike = 383800,
+ hitScheme = 383696,
+ shuffle = 322120,
+ invokeNiuzaoTheBlackOx = 132578,
+ weaponsOfOrder = 387184,
+ charredPassions = 386965,
+ bonedustBrew = 386276,
+ },
+ debuffs = {
+ breathOfFire = 146222,
+ moderateStagger = 124274,
+ heavyStagger = 124273,
+ kegSmash = 121253,
+ weaponsOfOrder = 387184,
+ bonedustBrew = 386276,
+
+ },
+ glyphs = {
+
+ },
+ talents = {
+ blackoutCombo = 196736,
+ blackOxBrew = 115399,
+ blackOxStatue = 115399,
+ bobAndWeave = 280515,
+ celestialFlame = 325177,
+ explodingKeg = 325153,
+ eyeOfTheTiger = 196607,
+ healingElixir = 122281,
+ highTolerance = 196737,
+ lightBrewing = 325093,
+ rushingJadeWind = 116847,
+ specialDelivery = 196730,
+ summonBlackOxStatue = 115315,
+ detox = 218164,
+ improvedPurifyingBrew = 343743,
+ invokeNiuzaoTheBlackOx = 132578,
+ improvedInvokeNiuzaoTheBlackOx = 322740,
+ weaponsOfOrder = 387184,
+ charredPassions = 386965,
+ pressTheAdvantage = 418359,
+ bonedustBrew = 386276,
+ },
+ },
+ -- Mistweaver
+ [270] = {
+ abilities = {
+
+ envelopingMist = 124682,
+ essenceFont = 191837,
+ invokeYulon = 322118,
+ reawaken = 212051,
+ renewingMist = 115151,
+ revival = 115310,
+ risingSunKick = 107428,
+ soothingMist = 115175,
+ thunderFocusTea = 116680,
+ invokeYulonTheJadeSerpent = 322118,
+ invokeChiJiTheRedCrane = 325197,
+ fortifyingBrew = 243435,
+ zenFocusTea = 209584,
+ transcendenceTransfer = 119996,
+ sheilunsGift = 399491,
+ detox = 115450,
+ manaTea = 115294,
+ },
+ artifacts = {
+ },
+ buffs = {
+ weaponsOfOrder = 310454,
+ fortifyingBrew = 243435,
+ soothingMist = 115175,
+ renewingMist = 115151,
+ envelopingMist = 124682,
+ thunderFocusTea = 116680,
+ lifeCyclesEnvelopingMist = 197919,
+ lifeCyclesVivify = 197916,
+ surgeOfMist = 246328,
+ danceOfMist = 247891,
+ upliftTrance = 197206,
+ refreshingJadeWind = 196725,
+ lifeCocoon = 116849,
+ transcendence = 101643,
+ tigersLust = 116841,
+ teachingsOfTheMonastery = 202090,
+ diffuseMagic = 122783,
+ dempenHarm = 122278,
+ innervate = 29166,
+ symbolOfHope = 64901,
+ wayOfTheCrane = 216113,
+ essenceFont = 191840,
+ risingMist = 22170,
+ soothingMistJadeStatue = 198533,
+ envelopingBreath = 325209,
+ invokeChiJiTheRedCrane = 343820,
+ ancientTeachingOfTheMonastery = 347553,
+ fallenOrder = 326860,
+ vivaciousVivification = 392883,
+ rushingJadeWind = 116847,
+ ancientTeachings = 388023,
+ sheilunsGift = 399491,
+ manaTea = 115867,
+ },
+ debuffs = {
+ mysticTouch = 113746,
+ },
+ glyphs = {
+ },
+ talents = {
+ diffuseMagic = 122783,
+ focusedThunder = 197895,
+ invokeChiJiTheRedCrane = 325197,
+ lifecycles = 197915,
+ mistWrap = 197900,
+ refreshingJadeWind = 196725,
+ risingMist = 274909,
+ songOfChiJi = 198898,
+ summonJadeSerpentStatue = 115313,
+ upwelling = 274963,
+ faelineStomp = 388193,
+ zenPulse = 124081,
+ ancienTeachings = 388023,
+ invokeYulonTheJadeSerpent = 322118,
+ lifeCocoon = 116849,
+ sheilunsGift = 399491,
+ spearHandStrike = 116705,
+ --manaTea = 197908,
+
+ },
+ runeforges = {
+ ancientTeachingsOfTheMonastery = 337172,
+ tearOfMorning = 337473,
+ }
+ },
+ -- Windwalker
+ [269] = {
+ abilities = {
+ disable = 116095,
+ fistsOfFury = 113656,
+ flyingSerpentKick = 101545,
+ flyingSerpentKickEnd = 115057,
+ fortifyingBrew = 243435,
+ invokeXuenTheWhiteTiger = 123904,
+ risingSunKick = 107428,
+ stormEarthAndFire = 137639,
+ stormEarthAndFireFixate = 221771,
+ touchOfKarma = 122470,
+ grappleWeapon = 233759,
+ fallenOrder = 326860,
+ flashcraft = 324631,
+ summonWhiteTigerStatue = 388686,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ alphaTiger = 287504,
+ fortifyingBrew = 243435,
+ invokersDelight = 338321,
+ blackoutKick = 116768,
+ chiEnergy = 337571,
+ danceOfChiJi = 325202,
+ hitCombo = 196741,
+ pressurePoint = 247255,
+ rushingJadeWind = 116847,
+ serenity = 152173,
+ stormEarthAndFire = 137639,
+ swiftRoundhouse = 278710,
+ theEmperorsCapacitor = 235054,
+ touchOfKarma = 122470,
+ transferThePower = 195321,
+ whirlingDragonPunch = 152175,
+ fallenOrder = 326860,
+ bonedustBrew = 325216,
+ teachingsOfTheMonastery = 202090,
+ },
+ debuffs = {
+ disable = 116095,
+ disableRoot = 116706,
+ markOfTheCrane = 228287,
+ mortalWounds = 115804,
+ risingFist = 242259,
+ skyreachExhaustion = 337341,
+ jadefireBrand = 395414,
+ },
+ glyphs = {
+ glyphOfRisingTigerKick = 125151,
+ },
+ talents = {
+ ascension = 115396,
+ danceOfChiJi = 325201,
+ diffuseMagic = 122783,
+ eyeOfTheTiger = 196607,
+ hitCombo = 196740,
+ rushingJadeWind = 116847,
+ serenity = 152173,
+ spiritualFocus = 280197,
+ strikeOfTheWindlord = 392983,
+ whirlingDragonPunch = 152175,
+ skyreach = 392991,
+ skytouch = 405044,
+ detox = 218164,
+ jadefireHarmony = 391412,
+ bonedustBrew = 386276,
+ stormEarthAndFire = 137639,
+ jadefireStomp = 388193,
+
+ },
+ traits = {
+ gloryOfTheDawn = 288634,
+ openPalmStrikes = 279918,
+ swiftRoundhouse = 277669,
+ },
+ runeforges = {
+ fatalTouch = 337296
+ }
+ },
+ -- All
+ Shared = {
+ abilities = {
+ blackoutKick = 100784,
+ cracklingJadeLightning = 117952,
+ expelHarm = 322101,
+ legSweep = 119381,
+ provoke = 115546,
+ resuscitate = 115178,
+ roll = 109132,
+ spinningCraneKick = 101546,
+ tigerPalm = 100780,
+ touchOfDeath = 322109,
+ touchOfFatality = 169340,
+ vivify = 116670,
+ zenFlight = 125883,
+ zenPilgrimage = 126892,
+ zenPilgrimageReturn = 126895,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ theEmperorsCapacitor = 235054,
+ transcendence = 101643,
+ weaponsOfOrder = 328908, --310454,
+ weaponsOfOrderWW = 310454, --311054,
+ prideful = 340880,
+ vivaciousVivification = 392883,
+ powerStrikes = 129914, --bonus chi proc buff
+ hitCombo = 196741,
+ kicksOfFlowingMomentum = 394944,
+ blackoutReinforcement = 424454,
+ },
+ conduits = {
+ calculatedStrikes = 336526,
+ coordinatedOffensive = 336598,
+ pustuleEruption = 351094
+ },
+ covenants = {
+ bonedustBrew = 325216,
+ faelineStomp = 327104,
+ fallenOrder = 326860,
+ weaponsOfOrder = 310454,
+ },
+ debuffs = {
+ bonedustBrew = 325216,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ bounceBack = 389577,
+ calmingPresence = 388664,
+ celerity = 115173,
+ chiBurst = 123986,
+ chiTorpedo = 115008,
+ chiWave = 115098,
+ closeToHeart = 389574,
+ dampenHarm = 122278,
+ disable = 116095,
+ elusiveMists = 388681,
+ escapeFromReality = 394110,
+ expeditiousFortification = 388813,
+ fastFeet = 388809,
+ fatalTouch = 394123,
+ ferocityOfXuen = 388674,
+ generousPour = 389575,
+ graceOfTheCrane = 388811,
+ hastyProvocation = 328670,
+ improvedParalysis = 344359,
+ improvedRoll = 328669,
+ improvedTouchOfDeath = 322113,
+ ironshellBrew = 388814,
+ paralysis = 115078,
+ profoundRebuttal = 392910,
+ resonantFists = 389578,
+ ringOfPeace = 116844,
+ risingSunKick = 107428,
+ saveThemAll = 389579,
+ spearHandStrike = 116705,
+ strengthOfSpirit = 387276,
+ summonWhiteTigerStatue = 388686,
+ tigerTailSweep = 264348,
+ tigersLust = 116841,
+ nimbleBrew = 137562,
+ transcendence = 101643,
+ vigorousExpulsion = 392900,
+ vivaciousVivication = 388812,
+ windwalking = 157411,
+ },
+ },
+ },
+ PALADIN = {
+ -- Holy
+ [65] = {
+ abilities = {
+ absolution = 212056,
+ auraMastery = 31821,
+ barrierOfFaith = 148039,
+ beaconOfFaith = 156910,
+ beaconOfLight = 53563,
+ beaconOfVirtue = 200025,
+ blessingOfAutumn = 388010,
+ blessingOfSpring = 388013,
+ blessingOfSummer = 388007,
+ blessingOfWinter = 388011,
+ cleanse = 4987,
+ divineProtection = 498,
+ holyLight = 82326,
+ holyShock = 20473,
+ lightOfDawn = 85222,
+ lightOfTheMartyr = 183998,
+ --lightsHammer = 114158,
+
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ --sealOfClarity = 384810,
+ avengingCrusader = 216331,
+ auraMastery = 31821,
+ avengingWrath = 31884,
+ avengingWrathCrit = 294027,
+ afterimage = 400745,
+ beaconOfLight = 53563,
+ beaconOfFaith = 156910,
+ blessingOfSacrifice = 6940,
+ beaconOfVirtue = 200025,
+ --bestowFaith = 223306,
+ --glimmerOfLight = 287280,
+ blessingOfDawn = 337747,
+ blessingOfDusk = 385126,
+ divineProtection = 498,
+ divinePurpose = 223819,
+ --ferventMartyr = 223316,
+ infusionOfLight = 54149,
+ innervate = 29166,
+ --ruleOfLaw = 214202,
+ liberation = 461471,
+ --theLightSaves = 200423,
+ vindicator = 200376,
+ symbolOfHope = 64901,
+ divineSteed = 254474,
+ --maraadsBreath = 340459,
+ unendingLight = 394709,
+ untemperedDedication = 339990,
+ veneration = 392939,
+ },
+ debuffs = {
+ judgement = 214222,
+ judgmentOfLight = 196941,
+ --glimmerOfLight = 287280,
+ consecration = 204242,
+ dawnWillCome = 364851,
+
+ },
+ glyphs = {
+
+ },
+ talents = {
+ avengingCrusader = 216331,
+ --awakening = 248033,
+ beaconOfFaith = 156910,
+ beaconOfVirtue = 200025,
+ --bestowFaith = 223306,
+ blessingOfSummer = 388007,
+ breakingDawn = 387879,
+ crusadersMight = 196926,
+ --glimmerOfLight = 325966,
+ holyPrism = 114165,
+ judgmentOfLight = 183778,
+ --lightsHammer = 114158,
+ --ruleOfLaw = 214202,
+ --sanctifiedWrath = 53376,
+ savedByTheLight = 157047,
+ divineToll = 375576,
+ },
+ traits = {
+ graceoftheJusticar = 278593,
+ indomitableJustice = 275496,
+ },
+ runeforges = {
+ shadowbreaker = 337812,
+ shockBarrier = 337825,
+ maraadsDyingBreath = 340458,
+ }
+ },
+ -- Protection
+ [66] = {
+ abilities = {
+ ardentDefender = 31850,
+ avengersShield = 31935,
+ blessedHammer = 229976,
+ cleanseToxins = 213644,
+ divineProtection = 498,
+ guardianOfAncientKings = 86659,
+ hammerOfTheRighteous = 53595,
+ judgment = 275779,
+ judgmentsOfTheWise = 105424,
+ rebuke = 96231,
+ righteousFury = 25780,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ ardentDefender = 31850,
+ avengingWrath = 31884,
+ blessingOfSpellwarding = 204018,
+ bulwarkOfOrder = 209388,
+ consecration = 188370,
+ divineShield = 642,
+ divinePurpose = 223819,
+ guardianOfAncientKings = 86659,
+ shieldOfTheRighteous = 132403,
+ shiningLight = 327510,
+ avengersValor = 197561,
+ divineSteed = 254474,
+ royalDecree = 340147,
+ },
+ debuffs = {
+ blessedHammer = 204301,
+ judgmentOfLight = 196941,
+ judgment = 197277,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ layOnHands = 633,
+ blessedHammer = 204019,
+ hammerOfTheRighteous = 53595,
+ innerLight = 386568,
+ redoubt = 280373,
+ holyShield = 152261,
+ grandCrusader = 85043,
+ -- shiningLight = 321136, -- No longer a talent?
+ consecratedGround = 204054,
+ improvedLayOnHands = 393027,
+ inspiringVanguard = 393022,
+ ardentDefender = 31850,
+ barricadeOfFaith = 385726,
+ consecrationInFlame = 379022,
+ improvedHolyShield = 393030,
+ sanctuary = 379021,
+ bulwarkOfOrder = 209389,
+ improvedArdentDefender = 393114,
+ blessingOfSpellwarding = 204018,
+ lightOfTheTitans = 378405,
+ strengthInAdversity = 393071,
+ crusadersResolve = 380188,
+ tyrsEnforcer = 378285,
+ relentlessInquisitor = 383388,
+ aveningWrathMight = 31884,
+ sentinel = 389539,
+ handOTheProtector = 315924,
+ strengthOfConviction = 379008,
+ resoluteDefender = 385422,
+ bastionOfLight = 378974,
+ guardianOfAncientKings = 86659,
+ crusadersJudgement = 204023,
+ uthersCounsel = 378425,
+ focusedEnmity = 378845,
+ soaringShield = 378457,
+ giftOfTheGoldenValkyr = 378279,
+ eyeOfTyr = 387174,
+ righteousProtector = 204074,
+ faithInTheLight = 379043,
+ ferrenMarcussFervor = 378762,
+ faithsArmor = 379017,
+ finalStand = 204077,
+ divineToll = 375576,
+ momentOfGlory = 327193,
+ bulwarkOfTheRighteousFury = 386653,
+ divineResonance = 386738,
+ quickenedInvocations = 379391,
+ },
+ traits = {
+ bulwarkOfLight = 272976,
+ },
+ },
+ -- Retribution
+ [70] = {
+ abilities = {
+ divineProtection = 403876,
+ templarSlash = 406647,
+ templarStrike = 407480,
+ templarsVerdict = 85256,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ crusade = 231895,
+ divineArbiter = 406975,
+ divinePurpose = 223819,
+ divineRight = 278523,
+ empyreanLegacy = 387178,
+ empyreanPower = 326733, --286393,
+ finalVerdict = 414949,
+ selflessHealer = 114250,
+ templarStrikes = 406648,
+ },
+ debuffs = {
+ executionSentence = 267798,
+ expurgation = 383346,
+ finalReckoning = 343721,
+ },
+ glyphs = {
+ glyphOfWingedVengeance = 57979,
+ },
+ talents = {
+ abjudication = 406157, --
+ aegisOfProtection = 403654, --
+ artOfWar = 406064, --
+ avengingWrathMight = 31884, --
+ bladeOfJustice = 184575, --
+ bladeOfVengeance = 403826, --
+ bladesOfLight = 403664, --
+ blessedChampion = 403010, --
+ boundlessJudgment = 405278, --
+ burningCrusade = 405289, --
+ consecratedBlade = 404834, --
+ consecratedGround = 204054, --
+ crusade = 231895, --
+ crusadingStrikes = 404542, --
+ divineArbiter = 404306, --
+ divineAuxiliary = 406158, --
+ divineHammer = 198034, --
+ divineStorm = 53385, --
+ divineWrath = 406872, --
+ empyreanLegacy = 387170, --
+ empyreanPower = 326732, --
+ executionSentence = 343527, --
+ executionersWill = 406940, --
+ expurgation = 383344, --
+ finalReckoning = 343721, --
+ finalVerdict = 383328, --
+ guidedPrayer = 404357, --
+ heartOfTheCrusader = 406154, --
+ highlordsJudgment = 404512, --
+ holyBlade = 383342, --
+ improvedBladeOfJustice = 403745, --
+ improvedJudgment = 405461, --
+ inquisitorsIre = 403975, --
+ judgeJuryAndExecutioner = 405607, --
+ judgmentOfJustice = 403495, --
+ jurisdiction = 402971, --
+ justicarsVengeance = 215661, --
+ lightOfJustice = 404436, --
+ lightsCelerity = 403698, --
+ penitence = 403026, --
+ righteousCause = 402912, --
+ rushOfLight = 407067, --
+ sanctify = 382536, --
+ searingLight = 404540, --
+ seethingFlames = 405355, --
+ shieldOfVengeance = 184662, --
+ swiftJustice = 383228, --
+ tempestOfTheLightbringer = 383396, --
+ templarStrikes = 406646, --
+ truthsWake = 403696, --
+ vanguardOfJustice = 406545, --
+ vanguardsMomentum = 383314, --
+ wakeOfAshes = 255937, --
+ zealotsFervor = 403509, --
+ },
+ traits = {
+ divineRight = 278523,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ arcaneTorrent = 155145, --
+ consecration = 26573, --
+ crusaderAura = 32223, --
+ crusaderStrike = 35395, --
+ divineShield = 642, --
+ flashOfLight = 19750, --
+ hammerOfJustice = 853, --
+ handOfReckoning = 62124, --
+ intercession = 391054, --
+ judgment = 20271, --
+ redemption = 7328, --
+ retributionAura = 183435, --
+ senseUndead = 5502, --
+ shieldOfTheRighteous = 53600, --
+ wordOfGlory = 85673, --
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ avengingWrath = 31884,
+ consecration = 26573,
+ concentrationAura = 317920,
+ crusaderAura = 32223,
+ devotionAura = 465,
+ divineResonance = 384029,
+ divineShield = 642,
+ divineSteed = 221883,
+ echoesOfWrath = 423590,
+ blessingOfProtection = 1022,
+ holyAvenger = 105809,
+ retributionAura = 183435,
+ seraphim = 152262,
+ vanquishersHammer = 328204,
+ },
+ conduits = {
+ expurgation = 339371,
+ templarsVindication = 339531,
+ },
+ covenants = {
+ --ashenHallow = 316958,
+ --blessingOfTheSeasons = 328278,
+ --blessingOfAutumn = 388010,
+ --blessingOfSpring = 388013,
+ --blessingOfSummer = 388007,
+ --blessingOfWinter = 388011,
+ --vanquishersHammer = 328204,
+ },
+ debuffs = {
+ blindingLight = 105421,
+ forbearance = 25771,
+ hammerOfJustice = 853,
+ judgment = 197277,
+ turnEvil = 10326,
+ handOfReckoning = 62124,
+ },
+ glyphs = {
+ glyphOfFireFromHeavens = 57954,
+ glyphOfPillarOfLight = 146959,
+ glyphOfTheLuminousCharger = 89401,
+ glyphOfTheQueen = 212642,
+ },
+ talents = {
+ afterimage = 385414, --
+ aurasOfSwiftVengeance = 385639, --
+ aurasOfTheResolute = 385633, --
+ avengingWrath = 31884, --
+ blessingOfFreedom = 1044, --
+ blessingOfProtection = 1022, --
+ blessingOfSacrifice = 6940, --
+ blindingLight = 115750, --
+ cavalier = 230332, --
+ cleanseToxins = 213644, --
+ crusadersReprieve = 403042, --
+ divinePurpose = 408459, --
+ divineResonance = 384027, --
+ divineSteed = 190784, --
+ divineToll = 375576, --
+ fadingLight = 405768, --
+ faithsArmor = 406101, --
+ fistOfJustice = 234299, --
+ goldenPath = 377128, --
+ greaterJudgment = 231663, --
+ hammerOfWrath = 24275, --
+ healingHands = 326734, --
+ holyAegis = 385515, --
+ improvedBlessingOfProtection = 384909, --
+ incandescence = 385464, --
+ judgmentOfLight = 183778, --
+ justification = 377043, --
+ layOnHands = 633, --
+ lightforgedBlessing = 403479, --
+ obduracy = 385427, --
+ ofDuskAndDawn = 385125, --
+ punishment = 403530, --
+ quickenedInvocation = 379391, --
+ rebuke = 96231, --
+ recompense = 384914, --
+ repentance = 20066, --
+ sacrificeOfTheJust = 384820, --
+ sanctifiedPlates = 402964, --
+ sealOfAlacrity = 385425, --
+ sealOfMercy = 384897, --
+ sealOfMight = 385450, --
+ sealOfOrder = 385129, --
+ sealOfTheCrusader = 385728, --
+ seasonedWarhorse = 376996, --
+ strengthOfConviction = 379008, --
+ touchOfLight = 385349, --
+ turnEvil = 10326, --
+ unboundFreedom = 305394, --
+ unbreakableSpirit = 114154, --
+ vengefulWrath = 406835, --
+ },
+ runeforges = {
+ finalVerdict = 337247,
+ theMadParagon = 337594,
+ theMagistratesJudgment = 337681,
+ vanguardsMomentum = 337638,
+ }
+ },
+ },
+ PRIEST = {
+ -- Discipline
+ [256] = {
+ abilities = {
+ angelicFeather = 121536,
+ --clarityOfWill = 152118,
+ divineStar = 110744,
+ desperatePrayer = 19236,
+ evangelism = 246287,
+ flashHeal = 2061,
+ halo = 120517,
+ holyNova = 132157,
+ leapOfFaith = 73325,
+ mindbender = 123040,
+ mindBlast = 8092,
+ mindControl = 205364,
+ mindSear = 48045,
+ mindSoothe = 453,
+ mindVision = 2096,
+ painSuppression = 33206,
+ penance = 47540,
+ --plea = 200829,
+ powerInfusion = 10060,
+ powerWordBarrier = 62618,
+ powerWordRadiance = 194509,
+ powerWordShield = 17,
+ powerWordSolace = 129250,
+ psychicScream = 8122,
+ purgeTheWicked = 204197,
+ purify = 527,
+ rapture = 47536,
+ schism = 214621,
+ shadowfiend = 34433,
+ shadowMend = 186263,
+ shiningForce = 204263,
+ sinsOfTheMany = 198076,
+ spiritShell = 109964,
+ smite = 585,
+ },
+ artifacts = {
+ --sinsOfTheMany = 198074,
+ },
+ buffs = {
+ angelicFeather = 121557,
+ atonement = 194384,
+ bodyAndSoul = 65081,
+ borrowedTime = 197763,
+ depthOfTheShadows = 275544,
+ --clarityOfWill = 152118,
+ innervate = 29166,
+ overloadedWithLight = 223166,
+ penitent = 246519,
+ --powerInfusion = 10060,
+ powerOfTheDarkSide = 198069,
+ powerWordShield = 17,
+ rapture = 47536,
+ speedOfThePious = 197767,
+ spiritShell = 109964,
+ symbolOfHope = 64901,
+ },
+ debuffs = {
+ weakenedSoul = 6788,
+ purgeTheWicked = 204213,
+ schism = 214621,
+ smite = 585,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ angelicFeather = 121536,
+ bodyAndSoul = 64129,
+ castigation = 193134,
+ --clarityOfWill = 152118,
+ contrition = 197419,
+ divineStar = 110744,
+ dominantMind = 205367,
+ evangelism = 246287,
+ --grace = 200309,
+ halo = 120517,
+ lenience = 238063,
+ masochism = 193063,
+ mindbender = 123040,
+ --powerInfusion = 10060,
+ powerWordSolace = 129250,
+ purgeTheWicked = 204197,
+ psychicVoice = 196704,
+ schism = 214621,
+ shadowCovenant = 314867,
+ shieldDiscipline = 197045,
+ spiritShell = 109964,
+ sinsOfTheMany = 280391,
+ shiningForce = 204263,
+ --thePenitent = 200347,
+ twistOfFate = 265259,
+ },
+ traits = {
+ giftOfForgiveness = 277680,
+ },
+ },
+ -- Holy
+ [257] = {
+ abilities = {
+ angelicFeather = 121536,
+ bodyAndMind = 214121,
+ circleOfHealing = 204883,
+ desperatePrayer = 19236,
+ divineHymn = 64843,
+ divineStar = 110744,
+ flashHeal = 2061,
+ guardianSpirit = 47788,
+ holyFire = 14914,
+ holyNova = 132157,
+ holyWordChastise = 88625,
+ holyWordSanctify = 34861,
+ holyWordSerenity = 2050,
+ holyWordSalvation = 265202,
+ heal = 2060,
+ leapOfFaith = 73325,
+ lightOfTuure = 33076,
+ prayerOfHealing = 596,
+ prayerOfMending = 33076,
+ purify = 527,
+ renew = 139,
+ smite = 585,
+ symbolOfHope = 64901,
+ },
+ artifacts = {
+ -- lightOfTuure = 208065,
+ },
+ buffs = {
+ angelicFeather = 121557,
+ blessingOfTuure = 196578,
+ divinity = 197031,
+ echoOfLight = 77489,
+ flashConcentration = 336267,
+ powerOfTheNaaru = 196489,
+ prayerOfMending = 41635,
+ renew = 139,
+ surgeOfLight = 114255,
+ spiritOfRedemption = 27827,
+ },
+ debuffs = {
+
+
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+ flashConcentration = 336266,
+ },
+ talents = {
+ angelicFeather = 121536,
+ apotheosis = 200183,
+ bodyAndSoul = 64129,
+ --bodyAndMind = 214121,
+ divineStar = 110744,
+ --divinity = 197031,
+ halo = 120517,
+ --piety = 197034,
+ shiningForce = 204263,
+ surgeOfLight = 109186,
+ --symbolOfHope = 64901,
+ },
+ },
+ -- Shadow
+ [258] = {
+ abilities = {
+ damnation = 341374,
+ darkAscension = 280711,
+ dispersion = 47585,
+ devouringPlague = 335467,
+ mindBlast = 8092,
+ mindBomb = 205369,
+ mindbender = 200174,
+ mindFlay = 15407,
+ mindSear = 48045,
+ mindVision = 2096,
+ powerInfusion = 10060,
+ powerWordShield = 17,
+ psychicHorror = 64044,
+ psychicScream = 8122,
+ purifyDisease = 213634,
+ shadowMend = 186263,
+ shadowWordVoid = 205351,
+ shadowfiend = 34433,
+ shadowform = 232698,
+ silence = 15487,
+ surrenderToMadness = 193223,
+ vampiricEmbrace = 15286,
+ vampiricTouch = 34914,
+ voidBolt = 205448,
+ devoidBolt = 343355,
+ voidEruption = 228260,
+ voidForm = 228264,
+ },
+ artifacts = {
+ lashOfInsanity = 238137,
+ massHysteria = 194378,
+ sphereOfInsanity = 194179,
+ toThePain = 193644,
+ touchOfDarkness = 194007,
+ unleashTheShadows = 194093,
+ voidCorruption = 194016,
+ voidTorrent = 205065,
+ },
+ buffs = {
+ chorusOfInsanity = 279572,
+ darkThoughts = 341207,
+ dispersion = 47585,
+ dissonantEchoes = 343144,
+ harvestedThoughts = 288343,
+ lingeringInsanity = 199849,
+ powerWordShield = 17,
+ powerInfusion = 10060,
+ shadowyInsight = 124430,
+ shadowform = 232698,
+ surrenderedSoul = 212570,
+ surrenderToMadness = 193223,
+ thoughtsHarvester = 288340,
+ unfurlingDarkness = 341282,
+ void = 211657,
+ voidForm = 194249,
+ voidTorrent = 205065,
+ zeksExterminatus = 236546, -- Legendary Cloak proc
+ },
+ debuffs = {
+ devouringPlague = 335467,
+ hungeringVoid = 345218,
+ mindFlay = 15407,
+ vampiricTouch = 34914,
+ weakenedSoul = 6788,
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+ shadowflamePrism = 336143,
+ },
+ talents = {
+ fortressOfTheMind = 193195,
+ deathAndMadness = 321291,
+ unfurlingDarkness = 341273,
+ bodyAndSoul = 64129,
+ sanlayn = 199855,
+ twistOfFate = 109142,
+ misery = 238558,
+ searingNightmare = 341385,
+ mindBomb = 205369,
+ psychicHorror = 64044,
+ auspiciousSpirits = 155271,
+ psychicLink = 199484,
+ shadowCrash = 205385,
+ damnation = 341374,
+ mindbender = 200174,
+ voidTorrent = 263165,
+ ancientMadness = 341240,
+ hungeringVoid = 345218,
+ surrenderToMadness = 319952,
+ },
+ traits = {
+ chorusOfInsanity = 278661,
+ deathThroes = 278659,
+ searingDialogue = 272788,
+ spitefulApparitions = 277682,
+ thoughtHarvester = 288340,
+ whispersOfTheDamned = 275722,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ ascendedBlast = 325283,
+ ascendedNova = 325020,
+ boonOfTheAscended = 325013,
+ desperatePrayer = 19236,
+ dispelMagic = 528,
+ fade = 586,
+ faeGuardians = 327661,
+ flashHeal = 2061,
+ leapOfFaith = 73325,
+ levitate = 1706,
+ massDispel = 32375,
+ massResurrection = 212036,
+ mindBlast = 8092,
+ mindControl = 605,
+ mindgames = 323673,
+ mindSoothe = 453,
+ mindVision = 2096,
+ powerInfusion = 10060,
+ powerWordFortitude = 21562,
+ powerWordShield = 17,
+ psychicScream = 8122,
+ resurrection = 2006,
+ shadowWordDeath = 32379,
+ shackleUndead = 9484,
+ shadowWordPain = 589,
+ smite = 585,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ classHallSpeed = 224098,
+ boonOfTheAscended = 325013,
+ faeGuardians = 327661,
+ levitate = 111759,
+ powerWordFortitude = 21562,
+ powerWordShield = 17,
+
+ },
+ conduits = {
+ dissonantEchoes = 338342,
+ },
+ covenants = {
+ boonOfTheAscended = 325013,
+ ascendedBlast = 325315,
+ ascendedEruption = 325326,
+ ascendedNova = 325020,
+ faeGuardians = 327661,
+ mindgames = 323673,
+ unholyNova = 324724,
+ },
+ debuffs = {
+ shadowWordPain = 589,
+ weakenedSoul = 6788,
+ wrathfulFaerie = 327703,
+
+ },
+ glyphs = {
+
+ },
+ talents = {
+
+ },
+ },
+ },
+ ROGUE = {
+ -- Assassination
+ [259] = {
+ abilities = {
+ -- ambush = 8676,
+ -- crimsonTempest = 121411,
+ -- deadlyPoison = 315584,
+ -- envenom = 32645,
+ -- eviscerate = 196819,
+ -- exsanguinate = 200806,
+ -- fanOfKnives = 51723,
+ -- garrote = 703,
+ -- mutilate = 1329,
+ -- poisonedKnife = 185565,
+ -- rupture = 1943,
+ -- shadowstep = 36554,
+ -- sinisterStrike = 1752,
+ -- vendetta = 79140,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ blindside = 121153,
+ deadlyPoison = 2823,
+ elaboratePlanning = 193641,
+ envenom = 32645,
+ leechingPoison = 108211,
+ sharpenedBlades = 272916,
+ masterAssassin = 256735,
+ subterfuge = 115192,
+ theDreadlordsDeceit = 208692,
+ },
+ debuffs = {
+ crimsonTempest = 121411,
+ deadlyPoison = 2818,
+ garrote = 703,
+ internalBleeding = 154953,
+ rupture = 1943,
+ surgeOfToxins = 192425,
+ toxicBlade = 245389,
+ vendetta = 79140,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ blindside = 328085,
+ crimsonTempest = 121411,
+ elaboratePlanning = 193640,
+ elusiveness = 79008,
+ exsanguinate = 200806,
+ hiddenBlades = 270061,
+ internalBleeding = 154904,
+ ironWire = 196861,
+ leechingPoison = 280716,
+ masterAssassin = 255989,
+ masterPoisoner = 196864,
+ nightstalker = 14062,
+ poisonBomb = 255544,
+ subterfuge = 108208,
+ venomRush = 152152,
+ },
+ traits = {
+ doubleDose = 273007,
+ echoingBlades = 287649,
+ sharpenedBlades = 272911,
+ shroudedSuffocation = 278666,
+ twistTheKnife = 273488,
+ },
+ runeforges = {
+ dashingScoundrel = 340081,
+ doomblade = 340082,
+ zoldyckInsignia = 340083,
+ duskwalkersPatch = 340084,
+ },
+ },
+ -- Outlaw
+ [260] = {
+ abilities = {
+ adrenalineRush = 13750,
+ ambush = 8676,
+ betweenTheEyes = 315341,
+ bladeFlurry = 13877,
+ bladeRush = 271877,
+ blind = 2094,
+ curseOfTheDreadblades = 202665,
+ dispatch = 2098,
+ ghostlyStrike = 196937,
+ gouge = 1776,
+ grapplingHook = 195457,
+ killingSpree = 51690,
+ masteryMainGauche = 76806,
+ pistolShot = 185763,
+ riposte = 199754,
+ rollTheBones = 315508,
+ sinisterStrike = 193315,
+ dreadblades = 343142,
+ },
+ artifacts = {
+ blackPowder = 216230,
+ bladeDancer = 202507,
+ bladeMaster = 202628,
+ blunderbuss = 202897,
+ blurredTime = 202769,
+ cannonballBarrage = 185767,
+ curseOfTheDreadblades = 202665,
+ cursedEdge = 202463,
+ cursedSteel = 214929,
+ deception = 202755,
+ fatesThirst = 202514,
+ fatebringer = 202524,
+ fortuneStrikes = 202530,
+ fortunesBoon = 202907,
+ fortunesStrike = 202521,
+ ghostlyShell = 202533,
+ greed = 202820,
+ gunslinger = 202522,
+ hiddenBlade = 202573,
+ killingSpree = 51690,
+ loadedDice = 238139,
+ },
+ buffs = {
+ adrenalineRush = 13750,
+ alacrity = 193538,
+ bladeFlurry = 13877,
+ blunderbuss = 202895,
+ broadside = 193356,
+ buriedTreasure = 199600,
+ deadShot = 272940,
+ grandMelee = 193358,
+ greenskinsWaterloggedWristcuffs = 209420,
+ hiddenBlade = 202754,
+ jollyRoger = 199603,
+ loadedDice = 256171,
+ opportunity = 195627,
+ ruthlessPrecision = 193357,
+ rollTheBones = {
+ broadside = 193356,
+ buriedTreasure = 199600,
+ grandMelee = 193358,
+ ruthlessPrecision = 193357,
+ skullAndCrossbones = 199603,
+ trueBearing = 193359,
+ },
+ sharkInfestedWaters = 193357,
+ skullAndCrossbones = 199603,
+ snakeeeyes = 275863, --typo, leaving in not to break stuff
+ snakeeyes = 275863,
+ swordplay = 211669,
+ trueBearing = 193359,
+ wits = 288988,
+ },
+ debuffs = {
+ ghostlyStrike = 196937,
+ gouge = 1776,
+ betweenTheEyes = 199804,
+ preyOnTheWeak = 255909,
+ blind = 2094,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ acrobaticStikes = 196924, -- typo, leaving in to not break old profiles
+ acrobaticStrikes = 196924,
+ dirtyTricks = 108216,
+ bladeRush = 271877,
+ ghostlyStrike = 196937,
+ grapplingHook = 256188,
+ hitAndRun = 196922,
+ ironStomach = 193546,
+ killingSpree = 51690,
+ weaponmaster = 200733,
+ quickDraw = 196938,
+ blindingPowder = 256165,
+ loadedDice = 256170,
+ dancingSteel = 272026,
+ dreadblades = 343142,
+ },
+ traits = {
+ deadshot = 272935,
+ aceupyoursleeve = 278676,
+ snakeeyes = 275846,
+ keepYourWitsAboutYou = 288979,
+
+ },
+ runeforges = {
+ greenskinsWickers = 340085,
+ guileCharm = 340086,
+ celerity = 340087,
+ concealedBlunderbuss = 340088,
+ },
+ conduits = {
+ countTheOdds = 341546,
+ },
+ },
+ -- Subtlety
+ [261] = {
+ abilities = {
+ backstab = 53,
+ blackPowder = 319175,
+ rupture = 1943,
+ shadowstrike = 185438,
+ shurikenStorm = 197835,
+ shurikenToss = 114014,
+ symbolsOfDeath = 212283,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ danseMacabre = 393969,
+ deeperDaggers = 383405,
+ finalityRupture = 385951,
+ flagellation = 384631,
+ flagellationPersist = 394758,
+ lingeringShadow = 385960,
+ masterAssassin = 256735,
+ masterOfShadows = 196980,
+ nightsVengeance = 273424,
+ perforatedVeins = 341572,
+ premeditation = 343160,
+ shadowBlades = 121471,
+ shadowDance = 185422,
+ sharpenedBlades = 272916,
+ shurikenCombo = 245640,
+ shurikenTornado = 277925,
+ subterfuge = 115192,
+ symbolsOfDeath = 212283,
+ symbolsOfDeathCrit = 227151,
+ shotInTheDark = 257506,
+ theDreadlordsDeceit = 228224,
+ theRotten = 394203,
+ },
+ debuffs = {
+ rupture = 1943,
+ findWeakness = 91021,
+ nightblade = 195452,
+ shadowsGrasp = 206760,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ cloakedInShadows = 382515, --
+ danseMacabre = 382528, --
+ darkBrew = 382504, --
+ darkShadow = 245687, --
+ deepeningShadows = 185314, --
+ deeperDaggers = 382517, --
+ ephemeralBond = 426563, --
+ exhilaratingExecution = 428486, --
+ fadeToNothing = 382514, --
+ finality = 382525, --
+ findWeakness = 91023, --
+ flagellation = 384631, --
+ gloomblade = 200758, --
+ goremawsBite = 426591, --
+ improvedBackstab = 319949, --
+ improvedShadowDance = 393972, --
+ improvedShadowTechniques = 394023, --
+ improvedShurikenStorm = 319951, --
+ inevitability = 382512, --
+ invigoratingShadowdust = 382523, --
+ lingeringShadow = 382524, --
+ masterOfShadows = 196976, --
+ nightTerrors = 277953, --
+ perforatedVeins = 382518, --
+ plannedExecution = 382508, --
+ premeditation = 343160, --
+ quickDecisions = 382503, --
+ relentlessStrikes = 58423, --
+ replicatingShadows = 382506, --
+ secretStratagem = 394320, --
+ secretTechnique = 280719, --
+ sepsis = 385408, --
+ shadowBlades = 121471, --
+ shadowFocus = 108209, --
+ shadowcraft = 426594, --
+ shadowedFinishers = 382511, --
+ shotInTheDark = 257505, --
+ shroudedInDarkness = 382507, --
+ shurikenTornado = 277925, --
+ silentStorm = 385722, --
+ swiftDeath = 394309, --
+ terrifyingPace = 428387, --
+ theFirstDance = 382505, --
+ theRotten = 382015, --
+ veiltouched = 382017, --
+ warningSigns = 426555, --
+ weaponmaster = 193537, --
+ withoutATrace = 382513, --
+ },
+ traits = {
+ bladeInTheShadows = 275896,
+ nightsVengeance = 273418,
+ replicatingShadows = 286121,
+ sharpenedBlades = 272911,
+ theFirstDance = 278681,
+ perforate = 277720,
+ inevitability = 278683,
+ },
+ runeforges = {
+ finality = 340089,
+ akaarisSoulFragment = 340090,
+ },
+ conduits = {
+ deeperDaggers = 341549,
+ perforatedVeins = 341567,
+ plannedExecution = 341556,
+ stilettoStaccato = 341559,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ ambush = 8676,
+ cheapShot = 1833,
+ crimsonVial = 185311,
+ cripplingPoison = 3408,
+ distract = 1725,
+ eviscerate = 196819,
+ feint = 1966,
+ instantPoison = 315584,
+ kick = 1766,
+ kidneyShot = 408,
+ pickLock = 1804,
+ pickPocket = 921,
+ sap = 6770,
+ shadowDance = 185313,
+ shroudOfConcealment = 114018,
+ sinisterStrike = 1752,
+ sliceAndDice = 315496,
+ sprint = 2983,
+ stealth = 1784,
+ vanish = 1856,
+ woundPoison = 8679,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ chaosBane = 356043,
+ cloakOfShadows = 31224,
+ coldBlood = 213981,
+ cripplingPoison = 3408,
+ deathFromAbove = 152150,
+ deathlyShadows = 341202,
+ feint = 1966,
+ flagellation = 345569,
+ flagellationPersist = 394758,
+ instantPoison = 315584,
+ leadByExample = 342156,
+ masterAssassinsMark = 340094,
+ numbingPoison = 5761,
+ sepsis = 328305,
+ shroudOfConcealment = 114018,
+ sliceAndDice = 315496,
+ sprint = 2983,
+ stealth = 1784,
+ stealthSepsis = 347037,
+ theRotten = 341134,
+ tricksOfTheTrade = 57934,
+ vanish = 1856,
+ woundPoison = 8679,
+ echoingReprimand = 323558,
+ thistleTea = 381623,
+ },
+ conduits = {
+ lashingScars = 341310,
+ plannedExecution = 341556,
+ reverberation = 341264,
+ septicShock = 341309,
+ },
+ covenants = {
+ echoingReprimand = 323547,
+ flagellation = 323654,
+ flagellationCleanse = 346975,
+ sepsis = 328305,
+ serratedBoneSpike = 328547,
+ },
+ debuffs = {
+ cheapShot = 1833,
+ cripplingPoison = 3408,
+ flagellation = 323654,
+ instantPoison = 315584,
+ kidneyShot = 408,
+ numbingPoison = 5761,
+ sap = 6770,
+ sepsis = 328305,
+ serratedBoneSpike = 328547,
+ serratedBoneSpikeDot = 324073,
+ shiv = 319504,
+ woundPoison = 8679,
+ },
+ glyphs = {
+ glyphOfBlackout = 219693,
+ glyphOfBurnout = 220279,
+ glyphOfDisguise = 63268,
+ glyphOfFlashBang = 219678,
+ },
+ talents = {
+ acrobaticStrikes = 196924, --
+ airbourneIrritant = 200733, --
+ alacrity = 193539, --
+ atrophicPoison = 381637, --
+ blackjack = 379005, --
+ blind = 2094, --
+ cheatDeath = 31230, --
+ cloakOfShadows = 31224, --
+ coldBlood = 382245, --
+ deadenedNerves = 231719, --
+ deadlyPrecision = 381542, --
+ deeperStratagem = 193531, --
+ echoingReprimand = 385616, --
+ elusiveness = 79008, --
+ evasion = 5277, --
+ featherfoot = 423683, --
+ fleetFooted = 378813, --
+ gouge = 1776, --
+ gracefulGuile = 423647, --
+ improvedAmbush = 381620, --
+ improvedSprint = 231691, --
+ improvedWoundPoison = 319066, --
+ ironStomach = 193546, --
+ lethality = 382238, --
+ leechingPoison = 280716, --
+ masterPoisoner = 378436, --
+ nightstalker = 14062, --
+ nimbleFingers = 378427, --
+ numbingPoison = 5761, --
+ recuperator = 378996, --
+ resoundingClarity = 381622, --
+ reverberation = 394332, --
+ rushedSetup = 378803, --
+ soothingDarkness = 393970, --
+ shadowDance = 185313, --
+ shadowrunner = 378807, --
+ shadowstep = 36554, --
+ shiv = 5938, --
+ stillshroud = 423662, --
+ subterfuge = 108208, --
+ superiorMixture = 423701, --
+ thistleTea = 381623, --
+ tightSpender = 381621, --
+ tricksOfTheTrade = 57934, --
+ unbreakableStride = 400804, --
+ vigor = 14983, --
+ virulentPoisons = 381543, --
+ },
+ runeforges = {
+ deathlyShadows = 340092,
+ essenceOfBloodfang = 340079,
+ invigoratingShadowdust = 340080,
+ markOfTheMasterAssassin = 340076,
+ obedience = 354703,
+ tinyToxicBlade = 340078,
+ theRotten = 340091,
+ },
+ },
+ },
+ SHAMAN = {
+ -- Elemental
+ [262] = {
+ abilities = {
+ earthquake = 61882,
+ earthShock = 8042,
+ eyeOfTheStorm = 157375,
+ fireBlast = 57984,
+ fireElemental = 198067,
+ hardenSkin = 118337,
+ immolate = 118297,
+ lavaBeam = 114074,
+ meteor = 117588,
+ pulverize = 118345,
+ windGust = 157331,
+ },
+ artifacts = {
+ },
+ conduits = {
+ callOfFlame = 338303,
+ },
+ buffs = {
+ ascendance = 114050,
+ bloodlust = 2825,
+ echoingShock = 320125,
+ elementalFocus = 16164,
+ elementalMastery = 16166,
+ emberTotem = 210658,
+ hardenSkin = 118337,
+ heroism = 32182,
+ iceFury = 210714,
+ lavaSurge = 77762,
+ masterOfTheElements = 260734,
+ powerOfTheMaelstrom = 191877,
+ resonanceTotem = 202192,
+ stormKeeper = 191634,
+ stormTotem = 210652,
+ surgeOfPower = 285514,
+ tailwindTotem = 210659,
+ tectonicThunder = 286949,
+ windGust = 263806,
+ },
+ debuffs = {
+ flameShock = 188389,
+ immolate = 118297,
+ lightningRod = 197209,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ aftershock = 273221,
+ ancestralGuidance = 108281,
+ ascendance = 114050,
+ earthenRage = 170374,
+ echoingShock = 320125,
+ echoOfTheElements = 333919,
+ elementalBlast = 117014,
+ iceFury = 210714,
+ liquidMagmaTotem = 192222,
+ masterOfTheElements = 16166,
+ primalElementalist = 117013,
+ staticDischarge = 342243,
+ stormElemental = 192249,
+ stormKeeper = 191634,
+ surgeOfPower = 262303,
+ unlimitedPower = 260895,
+
+ },
+ traits = {
+ naturalHarmony = 278697,
+ tectonicThunder = 286949,
+ },
+ },
+ -- Enhancement
+ [263] = {
+ abilities = {
+ tempest = 454015,
+ feralLunge = 196884,
+ --windfuryTotem = 8512,
+ windstrike = 115356, --17364,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ ascendance = 114051,
+ ashenCatalyst = 390371,
+ awakeningStorms = 462131,
+ cracklingSurge = 224127,
+ crashLightning = 187878,
+ clCrashLightning = 333964,
+ -- convergingStorms = 198300,
+ doomWinds = 384352,
+ earthenWeapon = 392375,
+ feralSpirit = 333957,
+ hailstorm = 334196,
+ hotHand = 215785,
+ icyEdge = 224126,
+ legacyOfTheFrostWitch = 384451,
+ maelstromOfElements = 394677,
+ moltenWeapon = 224125,
+ primordialWave = 327164,
+ splinteredElements = 354648,
+ tempest = 454015,
+ -- stormkeeper = 320137,
+ -- stormbringer = 201845,
+ },
+ debuffs = {
+ -- doomWinds = 335904,
+ lashingFlames = 334168,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ alphaWolf = 198434,
+ ancestralWolfAffinity = 382197,
+ ascendance = 114051,
+ ashenCatalyst = 390370,
+ convergingStorms = 384363,
+ crashLightning = 187874,
+ crashingStorms = 334308,
+ deeplyRootedElements = 378270,
+ doomWinds = 384352,
+ elementalAssault = 210853,
+ elementalBlast = 117014,
+ elementalSpirits = 262624,
+ elementalWeapons = 384355,
+ feralSpirit = 51533,
+ fireNova = 333974,
+ forcefulWinds = 262647,
+ hailstorm = 334195,
+ hotHand = 201900,
+ iceStrike = 342240,
+ improvedMaelstromWeapon = 383303,
+ lashingFlames = 334046,
+ lavaLash = 60103,
+ legacyOfTheFrostWitch = 384450,
+ moltenAssault = 334033,
+ overflowingMaelstrom = 384149,
+ primalMaelstrom = 384405,
+ primordialWave = 375982,
+ ragingMaelstrom = 384143,
+ refreshingWaters = 378211,
+ splinteredElements = 382042,
+ staticAccumulation = 384411,
+ stormblast = 319930,
+ stormflurry = 344357,
+ stormstrike = 17364,
+ stormsWrath = 392352,
+ sundering = 197214,
+ swirlingMaelstrom = 384359,
+ tempest = 454009,
+ tempestStrikes = 428071,
+ thorimsInvocation = 384444,
+ unrulyWinds = 390288,
+ windfuryWeapon = 33757,
+ witchDoctorsAncestry = 384447,
+ },
+ traits = {
+ lightningConduit = 275389,
+ naturalHarmony = 278697,
+ primalPrimer = 272992,
+ strengthOfTheEarth = 273461,
+ },
+ },
+ -- Restoration
+ [264] = {
+ abilities = {
+ ancestralVision = 212048,
+ waterShield = 52127,
+ healingRain = 73920,
+ healingTideTotem = 108280,
+ healingWave = 77472,
+ manaTideTotem = 16191,
+ purifySpirit = 77130,
+ riptide = 61295,
+ spiritLinkTotem = 98008,
+ recallCloudburstTotem = 201764,
+ },
+ artifacts = {
+ -- giftOfTheQueen = 207778,
+ },
+ buffs = {
+ ascendance = 114052,
+ cloudburstTotem = 157504,
+ waterShield = 52127,
+ healingRain = 73920,
+ heavyRainfall = 338344,
+ jonatsFocus = 210607,
+ lavaSurge = 77762,
+ riptide = 61295,
+ tidalWaves = 53390,
+ unleashLife = 73685,
+ undulation = 216251,
+ swirlingCurrents = 338340,
+ },
+ conduits = {
+ heavyRainfall = 338343,
+ },
+ debuffs = {
+ flameShock = 188389,
+ },
+ glyphs = {
+ },
+ talents = {
+ ancestralVigor = 207401,
+ ancestralProtectionTotem = 207399,
+ ascendance = 114052,
+ cloudburstTotem = 157153,
+ deluge = 200076,
+ downpour = 207778,
+ earthenWallTotem = 198838,
+ echoOfTheElements = 108283,
+ flashFlood = 280614,
+ highTide = 157154,
+ sugeofEarth = 320746,
+ torrent = 200072,
+ undulation = 200071,
+ unleashLife = 73685,
+ wellspring = 197995,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ ancestralSpirit = 2008,
+ astralRecall = 556,
+ bloodlust = 2825,
+ earthbindTotem = 2484,
+ farSight = 6196,
+ flameShock = 188389,
+ flametongueWeapon = 318038,
+ ghostWolf = 2645,
+ healingSurge = 8004,
+ heroism = 32182,
+ lightningBolt = 188196,
+ lightningShield = 192106,
+ primalStrike = 73899,
+ waterWalking = 546,
+ skyfury = 462854,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ astralShift = 108271,
+ earthShield = 974,
+ ghostWolf = 2645,
+ maelstromWeapon = 344179,
+ lightningShield = 192106,
+ skyfury = 462854,
+ spiritwalkersGrace = 79206,
+ waterWalking = 546,
+ },
+ conduits = {
+ astralProtection = 337964,
+ },
+ covenants = {
+ -- chainHarvest = 320674,
+ -- faeTransfusion = 328923,
+ -- primordialWave = 326059,
+ -- vesperTotem = 324386,
+ },
+ debuffs = {
+ flameShock = 188389,
+ frostShock = 196840,
+ hex = 51514,
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+ chainsOfDevastation = 336735,
+ doomWinds = 335902,
+ elementalEquilibrium = 336730,
+ primalLavaActuators = 335895,
+ echoesOfGreatSundering = 336215,
+ seedsOfRampantGrowth = 356218,
+ skybreakersFieryDemise = 336734,
+ spiritwalkersTidalTotem = 335891,
+ deeptremorStone = 336739,
+ windspeakersLavaResurgence = 336063,
+ deeplyRootedElements = 336738,
+ },
+ talents = {
+ ascendingAir = 462791,
+ ancestralGuidance = 108281,
+ arcticSnowstorm = 462764,
+ astralBulwark = 377933,
+ astralShift = 108271,
+ brimmingWithLife = 381689,
+ callOfTheElements = 383011,
+ capacitorTotem = 192058,
+ chainHeal = 1064,
+ chainLightning = 188443,
+ cleanseSpirit = 51886,
+ creationCore = 383012,
+ earthElemental = 198103,
+ earthgrabTotem = 51485,
+ earthShield = 974,
+ elementalOrbit = 383010,
+ elementalResistance = 462368,
+ elementalWarding = 381650,
+ encasingCold = 462762,
+ enhancedImbues = 462796,
+ fireAndIce = 382886,
+ flurry = 382888,
+ frostShock = 196840,
+ gracefulSpirit = 192088,
+ greaterPurge = 378773,
+ guardiansCudgel = 381819,
+ gustOfWind = 192063,
+ healingStreamTotem = 5394,
+ hex = 51514,
+ jetStream = 462817,
+ lavaBurst = 51505,
+ lightningLasso = 305483,
+ manaSpring = 381930,
+ naturesFury = 381655,
+ naturesGuardian = 30884,
+ naturesSwiftness = 378081,
+ planesTraveler = 381647,
+ poisonCleansingTotem = 383013,
+ purge = 370,
+ primordialBond = 381764,
+ seasonedWinds = 355630,
+ spiritWalk = 58875,
+ spiritwalkersAegis = 378077,
+ spiritwalkersGrace = 79206,
+ spiritWolf = 260878,
+ staticCharge = 265046,
+ stoneBulwarkTotem = 108270,
+ windwalkTotem = 108273,
+ thunderousPaws = 378075,
+ thundershock = 378779,
+ thunderstorm = 51490,
+ totemicFocus = 382201,
+ totemicProjection = 108287,
+ totemicRecall = 108285,
+ totemicSurge = 381867,
+ travelingStorms = 204403,
+ tremorTotem = 8143,
+ voodooMastery = 204268,
+ windsOfAlakir = 382215,
+ windRushTotem = 192077,
+ windShear = 57994,
+ windsOfAlAkir = 382215,
+ },
+ talentsHeroic = {
+ -- Enhance / Elemental Stormbringer
+ unlimitedPower = 454391,
+ supercharge = 455110,
+ voltaicSurge = 454919,
+ awakeningStorms = 455129,
+ stormcaller = 454021,
+ stormSwell = 455088,
+ arcDischarge = 455096,
+ conductiveEnergy = 455123,
+ shockingGrasp = 454022,
+ rollingThunder = 454026,
+ naturesProtection = 454027,
+ surgingCurrents = 454372,
+ -- Enhance / Restoration Totemic
+ surgingTotem = 444995,
+ totemicRebound = 445025,
+ oversizedTotems = 445026,
+ swiftRecall = 445027,
+ imbuementMastery = 445028,
+ whirlingElements = 445024,
+ amplificationCore = 445029,
+ oversurge = 445030,
+ windBarrier = 445031,
+ pulseCapacitor = 445032,
+ supportiveImbuements = 445033,
+ livelyTotems = 445034,
+ reactivity = 445035,
+ totemicCoordination = 445036,
+ earthsurge = 455590,
+ -- Elemental / Restoration Farseer
+ callOfTheAncestors = 443450,
+ latentWisdom = 443449,
+ ancientFellowship = 443423,
+ offeringFromBeyond = 443451,
+ naturalHarmony = 443442,
+ earthenCommunion = 443441,
+ ancestralSwiftness = 443454,
+ heedMyCall = 443444,
+ routineCommunication = 443445,
+ primordialCapacity = 443448,
+ maelstromSupremacy = 443447,
+ elementalReverb = 443418,
+ spiritwalkersMomentum = 443425,
+ finalCalling = 443446,
+
+
+ },
+ },
+ },
+ WARLOCK = {
+ -- Affliction
+ [265] = {
+ abilities = {
+ agony = 980,
+ commanddemon = 119898,
+ corruption = 172,
+ darkSoul = 113860,
+ deathbolt = 264106,
+ demonicGateway = 311699,
+ drainLife = 234153,
+ drainSoul = 198590,
+ felDomination = 333889,
+ grimoireOfSacrifice = 108503,
+ haunt = 48181,
+ maleficRapture = 324536,
+ mortalCoil = 6789,
+ phantomSingularity = 205179,
+ reapSouls = 216698,
+ seedOfCorruption = 27243,
+ shadowBolt = 232670,
+ shadowBolt2 = 686,
+ shadowLock = 171138,
+ siphonLife = 63106,
+ spellLock = 19647,
+ spellLockgrimoire = 132409,
+ soulEffigy = 205178,
+ soulRot = 325640,
+ soulSwap = 386951,
+ soulTap = 387073,
+ summonDarkglare = 205180,
+ unstableAffliction = 30108,
+ vileTaint = 278350,
+ },
+ artifacts = {
+ },
+ buffs = {
+ cascadingCalamity = 275378,
+ compoundingHorror = 199281,
+ darkSoul = 113860,
+ deadwindHarvester = 216708,
+ demonicPower = 196099,
+ empoweredLifeTap = 235156,
+ felDomination = 333889,
+ nightfall = 264571,
+ tormentedSouls = 216695,
+ wrathOfConsumption = 199646,
+ inevitableDemise = 273525,
+ },
+ debuffs = {
+ agony = 980,
+ corruption = 146739,
+ haunt = 48181,
+ phantomSingularity = 205179,
+ seedOfCorruption = 27243,
+ siphonLife = 63106,
+ shadowEmbrace = 32390,
+ unstableAffliction = 316099,
+ unstableAffliction2 = 233496,
+ unstableAffliction3 = 233497,
+ unstableAffliction4 = 233498,
+ unstableAffliction5 = 233499,
+ vileTaint = 278350,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ absoluteCorruption = 196103,
+ creepingDeath = 264000,
+ darkCaller = 334183,
+ darkfury = 264874,
+ seedOfCorruption = 196226,
+ darkSoul = 113860,
+ demonicSacrifice = 108503,
+ drainSoul = 198590,
+ grimoireOfSacrifice = 108503,
+ haunt = 48181,
+ howlOfTerror = 5484,
+ inevitableDemise = 334319,
+ nightfall = 108558,
+ phantomSingularity = 205179,
+ siphonLife = 63106,
+ soulConduit = 215941,
+ sowTheSeeds = 196226,
+ vileTaint = 278350,
+ writheInAgony = 196102,
+ },
+ traits = {
+ cascadingCalamity = 275372,
+ dreadfulCalling = 278727,
+ inevitableDemise = 273521,
+ pandemicInvocation = 289364,
+ }
+ },
+ -- Demonology
+ [266] = {
+ abilities = {
+ axeToss = 89766,
+ bilescourgeBombers = 267211,
+ callDreadstalkers = 104316,
+ commandDemon = 119898,
+ corruption = 172,
+ demonbolt = 264178,
+ demonicEmpowerment = 193396,
+ demonicStrength = 267171,
+ demonwrath = 193440,
+ doom = 603,
+ drainLife = 234153,
+ felFirebolt = 104318,
+ felstorm = 89751,
+ grimoireFelguard = 111898,
+ guillotine = 386833,
+ handOfGuldan = 105174,
+ implosion = 196277,
+ netherPortal = 267217,
+ powerSiphon = 264130,
+ shadowBolt = 686,
+ shadowflame = 205181,
+ shadowLock = 171138,
+ shadowsBite = 387322,
+ spellLock = 19647,
+ summonDarkglare = 205180,
+ summonDemonicTyrant = 265187,
+ summonVilefiend = 264119,
+ thalkielsConsumption = 211714,
+ },
+ artifacts = {
+ thalkielsAscendance = 238145,
+ thalkielsConsumption = 211714,
+ },
+ buffs = {
+ demonicCalling = 205146,
+ demonicCore = 267102,
+ demonicEmpowerment = 193396,
+ demonicPower = 265273,
+ demonwrath = 193440,
+ explosivePotential = 275398,
+ forbiddenKnowledge = 278738,
+ netherPortal = 267218,
+ shadowsBite = 272945,
+ shadowyInspiration = 196269,
+ },
+ debuffs = {
+ doom = 603,
+ shadowflame = 205181,
+ corruption = 172,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ bilescourgeBombers = 267211,
+ darkfury = 264874,
+ demonicStrength = 267171,
+ demonicCalling = 205145,
+ doom = 603,
+ dreadlash = 264078,
+ fromTheShadows = 267170,
+ grimoireFelguard = 111898,
+ howlOfTerror = 5484,
+ innerDemons = 267216,
+ netherPortal = 267217,
+ powerSiphon = 264130,
+ sacrificedSouls = 267214,
+ summonVilefiend = 264119,
+ soulConduit = 215941,
+
+ },
+ traits = {
+ balefulInvocation = 287059,
+ explosivePotential = 275395,
+ shadowsBite = 272944,
+ }
+ },
+ -- Destruction
+ [267] = {
+ abilities = {
+ cataclysm = 152108,
+ channelDemonfire = 196447,
+ chaosBolt = 116858,
+ conflagrate = 17962,
+ corruption = 172,
+ commandDemon = 119898,
+ darkSoul = 113858,
+ devourMagic = 19505,
+ dimensionalRift = 196586,
+ drainLife = 234153,
+ felDomination = 333889,
+ grimoireOfSacrifice = 108503,
+ havoc = 80240,
+ immolate = 348,
+ incinerate = 29722,
+ rainOfFire = 5740,
+ shadowBolt = 686,
+ shadowburn = 17877,
+ shadowLock = 171138,
+ singeMagic = 119905,
+ singeMagicGrimoire = 132411,
+ soulFire = 6353,
+ spellLock = 19647,
+ spellLockgrimoire = 132409,
+ summonInfernal = 1122,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ backdraft = 117828, --196406,
+ crashingChaos = 277706,
+ darkSoul = 113858,
+ darkSoulInstability = 113858,
+ demonicPower = 196099,
+ empoweredLifeTap = 235156,
+ felDomination = 333889,
+ lessonsOfSpaceTime = 236174,
+ lordOfFlames = 224103,
+ },
+ debuffs = {
+ conflagrate = 265931,
+ eradication = 196414,
+ immolate = 157736,
+ havoc = 80240,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ cataclysm = 152108,
+ channelDemonfire = 196447,
+ darkFury = 264874,
+ darkSoul = 113858,
+ darkSoulInstability = 113858,
+ eradication = 196412,
+ flashover = 267115,
+ fireAndBrimstone = 196408,
+ grimoireOfSacrifice = 108503,
+ grimoireOfSupremacy = 266086,
+ howlOfTerror = 5484,
+ inferno = 270545,
+ internalCombustion = 266134,
+ reverseEntropy = 205148,
+ roaringBlaze = 205184,
+ shadowburn = 17877,
+ soulFire = 6353,
+ soulConduit = 215941,
+ },
+ traits = {
+ crashingChaos = 277644
+ }
+ },
+ -- Inital Warlock (1-10)
+ [1454] = {
+ abilities = {
+ controlDemon = 93375,
+ corruption = 172,
+ curseOfWeakness = 702,
+ drainLife = 234153,
+ shadowBolt = 686,
+ },
+ buffs = {
+
+ },
+ debuffs = {
+ corruption = 146739,
+ curseOfWeakness = 702,
+ drainLife = 234153,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ amplifyCurse = 328774,
+ banish = 710,
+ burningRush = 111400,
+ curseOfExhaustion = 334275,
+ curseOfTongues = 1714,
+ curseOfWeakness = 702,
+ createHealthstone = 6201,
+ createSoulwell = 29893,
+ commandDemon = 119898,
+ corruption = 172,
+ darkPact = 108416,
+ demonicGateway = 111771,
+ demonicCircle = 48018,
+ demonicTeleport = 48020,
+ devourMagic = 19505,
+ eyeOfKilrogg = 126,
+ felDomination = 333889,
+ fear = 5782,
+ grimoireFelhunter = 111897,
+ grimoireImp = 111859,
+ grimoireOfService = 108501,
+ grimoireSuccubus = 111896,
+ grimoireVoidwalker = 111895,
+ healthFunnel = 755,
+ inquisitorsGaze = 386344,
+ howlOfTerror = 5484,
+ lifeTap = 1454,
+ mortalCoil = 6789,
+ ritualOfDoom = 342601,
+ ritualOfSummoning = 698,
+ shadowflame = 384069,
+ shadowfury = 30283,
+ shadowBulwark = 119907,
+ spellLock = 19647,
+ seduction = 6358,
+ singeMagic = 89808,
+ soulburn = 385899,
+ soulHarvest = 196098,
+ soulstone = 20707,
+ summonSoulkeeper = 386256,
+ --summonDoomguard = 18540,
+ subjugateDemon = 1098,
+ summonFelguard = 30146,
+ summonFelhunter = 691,
+ summonFelImp = 688,
+ summonImp = 688,
+ summonInfernal = 1122,
+ summonSuccubus = 366222,
+ summonVoidwalker = 697,
+ summonWrathguard = 112870,
+ unendingBreath = 5697,
+ unendingResolve = 104773,
+ unstableaffliction = 316099,
+
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ burningRush = 111400,
+ demonicCircle = 48018,
+ demonicSynergy = 171982,
+ felDomination = 333889,
+ healthFunnel = 755,
+ grimoireOfSacrifice = 196099,
+ sindoreiSpite = 208871,
+ soulHarvest = 196098,
+ soulstone = 20707,
+ unendingBreath = 5697,
+ unendingResolve = 104773,
+ },
+ conduits = {
+ corruptingLeer = 339455,
+
+ },
+ covenants = {
+ decimatingBolt = 325289,
+ impendingCatastrophe = 321792,
+ scouringTithe = 312321,
+ },
+ debuffs = {
+ decimatingBolt = 325289,
+ impendingCatastrophe = 321792,
+ scouringTithe = 312321,
+ soulRot = 325640,
+ fear = 5782,
+ curseOfExhaustion = 334275,
+ curseOfTongues = 1714,
+ curseOfWeakness = 702,
+ },
+ glyphs = {
+ glyphOfTheFelImp = 219424,
+ },
+ talents = {
+ burningRush = 111400,
+ darkPact = 108416,
+ demonSkin = 219272,
+ mortalCoil = 6789,
+
+ },
+ },
+ },
+ WARRIOR = {
+ -- Arms
+ [71] = {
+ abilities = {
+ sweepingStrikes = 260708,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ ashenJuggernaut = 335234,
+ avatar = 389748,
+ battlelord = 346369,
+ battleStance = 386164,
+ bladestorm = 446035,
+ collateralDamage = 334783,
+ colossusSmash = 208086,
+ crushingAssault = 278826,
+ deadlyCalm = 262228,
+ defensiveStance = 386208,
+ exploiter = 335452,
+ imminentDemise = 445606,
+ inForTheKill = 215550,
+ juggernaut = 383290,
+ mercilessBonegrinder = 383316,
+ opportunist = 456120,
+ overpower = 400805,
+ recklessness = 389722,
+ stoneHeart = 225947,
+ suddenDeath = 52437,
+ sweepingStrikes = 260708,
+ testOfMight = 275540,
+ },
+ conduits = {
+ ashenJuggernaut = 335232,
+ },
+ covenants = {
+ condemn = 317349,
+ condemnMassacre = 330334,
+ },
+ debuffs = {
+ colossusSmash = 208086,
+ deepWounds = 262115,
+ executionersPrecision = 272870,
+ markedForExecution = 445584,
+ hamstring = 1715,
+ ravager = 227876,
+ rend = 772,
+ },
+ glyphs = {
+ glyphOfThunderStrike = 68164,
+ },
+ talents = {
+ armoredToTheTeeth = 384124,
+ arterialBleed = 440995,
+ avatar = 107574,
+ barbaricTraining = 383082,
+ battleStance = 386164,
+ battlelord = 386630,
+ blademastersTorment = 390138,
+ bladestorm = 227847,
+ bloodborne = 383287,
+ bloodletting = 383154,
+ bloodsurge = 384361,
+ bluntInstruments = 383442,
+ boneshaker = 429639,
+ brutalFinish = 446085,
+ cleave = 845,
+ collateralDamage = 334779,
+ colossalMight = 429634,
+ colossusSmash = 167105,
+ criticalThinking = 389306,
+ crushingForce = 382764,
+ cullingCyclone = 444778,
+ danceOfDeath = 390713,
+ -- deadlyCalm = 262228,
+ deathDrive = 444770,
+ deftExperience = 389308,
+ demolish = 436358,
+ dieByTheSword = 118038,
+ dominanceOfTheColossus = 429636,
+ dreadnaught = 262150,
+ earthquaker = 440992,
+ enduranceTraining = 382940,
+ executionersPrecision = 386634,
+ exhilaratingBlows = 383219,
+ fatality = 383703,
+ fervorOfBattle = 202316,
+ fierceFollowthrough = 444773,
+ finishingBlows = 400205,
+ frothingBerserker = 392792,
+ fueledByViolence = 383103,
+ ignorePain = 190456,
+ imminentDemise = 444769,
+ impale = 383430,
+ improvedExecute = 316405,
+ improvedOverpower = 385571,
+ improvedSweepingStrikes = 383155,
+ inForTheKill = 248621,
+ juggernaut = 383292,
+ martialExpert = 429638,
+ martialProwess = 316440,
+ massacre = 281001,
+ mercilessBonegrinder = 383317,
+ mortalStrike = 12294,
+ mountainOfMuscleAndScars = 429642,
+ noStrangerToPain = 429644,
+ oneAgainstMany = 429637,
+ opportunist = 444774,
+ overpower = 7384,
+ overwhelmingBlades = 444772,
+ practicedStrikes = 429647,
+ preciseMight = 431548,
+ reapTheStorm = 444775,
+ relentlessPursuit = 444776,
+ rend = 772,
+ secondWind = 29838,
+ sharpenedBlades = 383341,
+ showNoMercy = 444771,
+ skullsplitter = 260643,
+ slayersDominance = 444767,
+ slayersMalice = 444779,
+ spitefulSerenity = 400314,
+ stormOfSwords = 385512,
+ stormWall = 388807,
+ strengthOfArms = 400803,
+ suddenDeath = 29725,
+ tactician = 184783,
+ testOfMight = 385008,
+ tideOfBattle = 429641,
+ twohandedWeaponSpecialization = 382896,
+ unhinged = 386628,
+ unrelentingOnslaught = 444780,
+ valorInVictory = 383338,
+ veteranVitality = 440993,
+ viciousAgility = 444777,
+ warMachine = 262231,
+ warbreaker = 262161,
+ warlordsTorment = 390140,
+ },
+ traits = {
+ seismicWave = 277639,
+ testOfMight = 275529,
+ }
+ },
+ -- Fury
+ [72] = {
+ abilities = {
+ crushingBlow = 335097,
+ enrage = 184361,
+ execute = 5308,
+ whirlwind = 190411,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ ashenJuggernaut = 392537,
+ avatar = 107574,
+ berserkerStance = 386196,
+ bladestorm = 446035,
+ bloodbath = 461288,
+ brutalFinish = 446918,
+ burstOfPower = 437121,
+ crushingBlow = 396752,
+ -- deathWish = 199261,
+ enrage = 184362,
+ enragedRegeneration = 184364,
+ -- frenzy = 335082,
+ meatCleaver = 85739,
+ -- mercilessBonegrinder = 346574,
+ opportunist = 456120,
+ recklessness = 1719,
+ suddenDeath = 280776,
+ -- whirlwind = 85739,
+ },
+ conduits = {
+
+ },
+ covenants = {
+ condemn = 317485,
+ condemnMassacre = 330325,
+ },
+ debuffs = {
+ championsMight = 386286,
+ markedForExecution = 445584,
+ odynsFuryTormentMh = 385060,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ armoredToTheTeeth = 384124,
+ ashenJuggernaut = 392536,
+ avatar = 107574,
+ avatarOfTheStorm = 437134,
+ barbaricTraining = 390674,
+ berserkerStance = 386196,
+ berserkersTorment = 390123,
+ bladestorm = 227847,
+ bloodborne = 385703,
+ bloodcraze = 393950,
+ bloodthirst = 23881,
+ brutalFinish = 446085,
+ burstOfPower = 437118,
+ coldSteelHotBlood = 383959,
+ crashingThunder = 436707,
+ criticalThinking = 383297,
+ cruelty = 392931,
+ crushingForce = 382764,
+ cullingCyclone = 444778,
+ dancingBlades = 391683,
+ deathDrive = 444770,
+ deftExperience = 383295,
+ depthsOfInsanity = 383922,
+ -- dragonRoar = 118000,
+ dualWieldSpecialization = 382900,
+ enduranceTraining = 391997,
+ enragedRegeneration = 184364,
+ fierceFollowthrough = 444773,
+ flashingSkies = 437079,
+ focusInChaos = 383486,
+ frenziedEnrage = 383848,
+ frenzy = 335077,
+ freshMeat = 215568,
+ frothingBerserker = 215571,
+ -- furiousCharge = 202224,
+ gatheringClouds = 436201,
+ groundCurrent = 436148,
+ hackAndSlash = 383877,
+ imminentDemise = 444769,
+ improvedBloodthirst = 383852,
+ improvedExecute = 316402,
+ improvedRagingBlow = 383854,
+ improvedWhirlwind = 12950,
+ invigoratingFury = 383468,
+ keepYourFeetOnTheGround = 438590,
+ lightningStrikes = 434969,
+ massacre = 206315,
+ meatCleaver = 280392,
+ odynsFury = 385059,
+ onslaught = 315720,
+ opportunist = 444774,
+ overwhelmingBlades = 444772,
+ powerfulEnrage = 440277,
+ ragingBlow = 85288,
+ rampage = 184367,
+ reapTheStorm = 444775,
+ recklessAbandon = 396749,
+ recklessness = 1719,
+ relentlessPursuit = 444776,
+ -- seethe = 335091,
+ showNoMercy = 444771,
+ -- siegebreaker = 280772,
+ singlemindedFury = 81099,
+ slaughteringStrikes = 388004,
+ slayersDominance = 444767,
+ slayersMalice = 444779,
+ snapInduction = 456270,
+ steadfastAsThePeaks = 434970,
+ stormBolts = 436162,
+ stormOfSteel = 382953,
+ stormShield = 438597,
+ strengthOfTheMountain = 437068,
+ suddenDeath = 280721,
+ swiftStrikes = 383459,
+ tenderize = 388933,
+ thorimsMight = 436152,
+ thunderBlast = 435607,
+ titansTorment = 390135,
+ titanicRage = 394329,
+ unbridledFerocity = 389603,
+ unhinged = 386628,
+ unrelentingOnslaught = 444780,
+ viciousAgility = 444777,
+ viciousContempt = 383885,
+ warMachine = 346002,
+ warpaint = 208154,
+ wrathAndFury = 392936,
+ },
+ traits = {
+ coldSteelHotBlood = 288080
+ }
+ },
+ -- Protection
+ [73] = {
+ abilities = {
+ demoralizingShout = 1160,
+ devastate = 20243,
+ focusedRage = 204488,
+ impendingVictory = 202168,
+ intercept = 198304,
+ lastStand = 12975,
+ ravager = 228920,
+ revenge = 6572,
+ shieldBlock = 2565,
+ shieldSlam = 23922,
+ shieldWall = 871,
+ shockwave = 46968,
+ thunderClap = 6343,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ avatar = 107574,
+ defensiveStance = 71,
+ lastStand = 12975,
+ revenge = 5302,
+ shieldBlock = 132404,
+ shieldWall = 871,
+ vengeanceFocusedRage = 202573,
+ vengeanceIgnorePain = 202574,
+ vengeanceRevenge = 202573,
+ victorious = 32216,
+ },
+ conduits = {
+
+ },
+ covenants = {
+ condemn = 317349,
+ },
+ debuffs = {
+ deepwoundsProt = 115767,
+ demoralizingShout = 1160,
+ thunderClap = 6343,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ bestServedCold = 202560,
+ warMachine = 316733,
+ bolster = 280001,
+ boomingVoice = 202743,
+ cracklingThunder = 203201,
+ devastator = 236279,
+ dragonRoar = 118000,
+ heavyRepercussions = 203177,
+ indomitable = 202095,
+ intoTheFray = 202603,
+ menace = 275338,
+ neverSurrender = 202561,
+ punish = 275334,
+ ravager = 228920,
+ rumblingEarth = 275339,
+ unstoppableForce = 275336,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ attack = 88163,
+ battleShout = 6673,
+ berserkerRage = 18499,
+ charge = 100,
+ execute = 163201,
+ hamstring = 1715,
+ heroicThrow = 57755,
+ pummel = 6552,
+ shieldBlock = 2565,
+ shieldSlam = 23922,
+ slam = 1464,
+ taunt = 355,
+ victoryRush = 34428,
+ whirlwind = 1680,
+ },
+ animas = {
+ bloodstainedWhetstone = 322036,
+ hurricaneHeart = 329122,
+ championsDecree = 322054,
+ periaptOfFuror = 322049,
+ pleonexianCommand = 329165,
+ rageMote = 322046,
+ weatheredRunestone = 349916,
+ singingStones = 329489,
+ singingStonesOfIntimidation = 329484,
+ singingStonesOfUnnerving = 329488,
+ tarnishedMedallion = 348483,
+ thunderingRoar = 322093,
+ zovaalsWarbanner = 322027,
+ bladedBulwark = 322026,
+ fanOfLongswords = 322029,
+ misshapenMirror = 329268,
+ shriekingFlagon = 329275,
+ voraciousCullingBlade = 329213,
+ kyrianWarhelm = 314290,
+ scratchedKnife = 322039,
+ signetOfTormentedKings = 329345,
+ smolderingInertia = 322032,
+ umbralEarTrumpet = 329283,
+ warlordsResolve = 322051,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ battleShout = 6673,
+ berserkerRage = 18499,
+ firstStrike = 325381,
+ fujiedasFury = 207776,
+ ignorePain = 190456,
+ outburst = 364010,
+ seeingRed = 364006,
+ smolderingInertia = 322032,
+ spellReflection = 23920,
+ victorious = 32216,
+ },
+ conduits = {
+ viciousContempt = 337302,
+ destructiveReverberations = 339939,
+ },
+ covenants = {
+ ancientAftershock = 325886,
+ conquerorsBanner = 324143,
+ fleshcraft = 324631,
+ spearOfBastion = 307865,
+ },
+ debuffs = {
+ markOfFyralath = 414532,
+ },
+ glyphs = {
+
+ },
+ items = {
+ treacherousTransmitter = 221023,
+ },
+ runeforges = {
+ battlelord = 335274,
+ cadenceOfFujieda = 335555,
+ deathmaker = 335567,
+ elysianMight = 357996,
+ enduringBlow = 335458,
+ exploiter = 335451,
+ leaper = 335214,
+ misshapenMirror = 335253,
+ recklessDefence = 335582,
+ reprisal = 335718,
+ seismicReverberation = 335758,
+ signetOfTormentedKings = 335266,
+ sinfulSurge = 354131,
+ theWall = 335239,
+ thunderlord = 335229,
+ unhinged = 335282,
+ willOfTheBerserker = 335594,
+ unbreakableWill = 335629,
+ },
+ talents = {
+ angerManagement = 152278,
+ berserkerShout = 384100,
+ bitterImmunity = 383762,
+ boundingStride = 202163,
+ cacophonousRoar = 382954,
+ championsMight = 386284,
+ championsSpear = 376079,
+ concussiveBlows = 383115,
+ cracklingThunder = 203201,
+ cruelStrikes = 392777,
+ defensiveStance = 386208,
+ -- demoralizingShout = 316464,
+ doubleTime = 103827,
+ fastFootwork = 382260,
+ heroicLeap = 6544,
+ honedReflexes = 391271,
+ impendingVictory = 202168,
+ intervene = 3411,
+ intimidatingShout = 5246,
+ leechingStrikes = 382258,
+ menace = 275338,
+ overwhelmingRage = 382767,
+ painAndGain = 382549,
+ piercingChallenge = 382948,
+ piercingHowl = 12323,
+ rallyingCry = 97462,
+ ravager = 228920,
+ reinforcedPlates = 382939,
+ rumblingEarth = 275339,
+ secondWind = 29838,
+ seismicReverberation = 382956,
+ shatteringThrow = 64382,
+ -- shieldWall = 316834,
+ shockwave = 46968,
+ sidearm = 384404,
+ spellReflection = 23920,
+ stormBolt = 107570,
+ thunderClap = 6343,
+ thunderousRoar = 384318,
+ thunderousWords = 384969,
+ uproar = 391572,
+ wildStrikes = 382946,
+ wreckingThrow = 384110,
+ },
+ },
+ },
+ -- Global
+ Shared = {
+ Shared = {
+ abilities = {
+ autoAttack = 6603,
+ autoShot = 75,
+ giftOfTheNaaru = br.functions.spell:getRacial("Draenei"), --select(7, GetSpellInfo(GetSpellInfo(28880))),
+ global = 61304,
+ heartOfAzeroth = 296208, -- used for isActiveEssence icon comparison
+ latentArcana = 296971,
+ lightsJudgment = 255647,
+ quakingPalm = 107079,
+ racial = br.functions.spell:getRacial(),
+ shadowmeld = 58984,
+ },
+ animas = {
+ scrollOfElchaver = 305006,
+ },
+ artifacts = {
+ artificialDamage = 226829,
+ artificialStamina = 211309,
+ concordanceOfTheLegionfall = 239042,
+ },
+ buffs = {
+ ancientHysteria = 90355,
+ battlePotionOfAgility = 279152,
+ battlePotionOfIntellect = 279151,
+ battlePotionOfStrength = 279153,
+ superiorBattlePotionOfAgility = 298146,
+ potionOfUnbridledFury = 300714,
+ blessingOfSacrifice = 6940,
+ -- greaterBlessingOfKings = 203538,
+ -- greaterBlessingOfWisdom = 203539,
+ battleScarredAugmentation = 270058, -- BfA Augment Rune Buff
+ blessingOfFreedom = 1044,
+ blessingOfProtection = 1022,
+ bloodLust = {
+ ancientHysteria = 90355,
+ bloodlust = 2825,
+ drumsOfRage = 146555,
+ drumsOfTheMaelstrom = 256740,
+ drumsOfTheMountain = 230935,
+ heroism = 32182,
+ netherwinds = 160452,
+ primalRage = 264667,
+ timewarp = 80353,
+ },
+ concordanceOfTheLegionfall = 239042,
+ defiledAugmentation = 224001, -- Lightforged Augment Rune buff
+ felFocus = 242551,
+ flaskOfTenThousandScars = 188035,
+ flaskOfTheCountlessArmies = 188034,
+ flaskOfTheSeventhDemon = 188033,
+ flaskOfTheWhisperedPact = 188031,
+ flaskOfTheCurrents = 251836,
+ flaskOfEndlessFathoms = 251837,
+ flaskOfTheVastHorizon = 251838,
+ flaskOfTheUndertow = 251839,
+ flayedWingToxin = 345546,
+ forTheAlliance = 193863, -- BfA seasonal event buff
+ forTheHorde = 193864, -- BfA seasonal event buff
+ fruitfulMachinatins = 242623, -- Absorb Shield from Deceiver's Grand Design
+ mistcallerOcarina = 330067, -- SL Trinket with group buff
+ greaterFlaskOfEndlessFathoms = 298837,
+ greaterFlaskOfTheCurrents = 298836,
+ greaterFlaskOfTheUndertow = 298841,
+ greaterFlaskOfTheVastHorizon = 298839,
+ guidingHand = 242622, -- from The Deceiver's Grand Design
+ heroism = 32182,
+ ineffableTruth = 316801,
+ latentArcana = 296962,
+ netherwinds = 160452,
+ norgannonsForesight = 236373,
+ overchargeMana = 299624,
+ potionOfBurstingBlood = 265514,
+ potionOfFocusedResolve = 298317,
+ prolongedPower = 229206,
+ queensDecreeUnstoppable = 302417, -- M+ Beguiling affix: prevents interrupt
+ racial = br.functions.spell:getRacial(),
+ razorCoral = 303570, -- Crit Buff from Ashvane's Razor Coral
+ sephuz1 = 208051, -- the fulltime 10% movement, 2% haste buff
+ sephuz2 = 208052, -- the proc, 70% movement, 25% haste buff
+ sephuzCooldown = 226262, -- CD (30 seconds) for the proc
+ shadowmeld = 58984,
+ symbolOfHope = 64901,
+ timeWarp = 80353,
+ whispersOfInsanity = 176151,
+ cracklingTourmaline = 290372,
+ saphireofBrilliance = 290365,
+ vigorEngaged = 287916,
+ soulshape = 310143,
+ soulIgnition = 345251,
+ spectralFlaskOfPower = 307185,
+ elementalPotionOfPower = 371024,
+ elementalPotionOfUltimatePower = 371028,
+ phialOfGlacialFury = 373257,
+ phialOfTepidVersatility = 371172,
+ icedPhialOfCorruptingRage = 374000,
+ domineeringArrogance = 411661,
+
+ },
+ --TODO Add to API
+ -- itemEnchantments = {
+ -- --Dragonflight weapon Imbue's from inscription enchantments
+ -- --Quality of aura should always be GOLD,SILVER,COPPER
+ -- buzzingRune = { 6514, 6513, 6512 },
+ -- chirpingRune = { 6695, 6694, 6515 },
+ -- howlingRune = { 6518, 6517, 6516 },
+ -- hissingRune = { 6839, 6837, 6838 },
+ -- },
+ conduits = {
+
+ },
+ covenants = {
+ covenantAbility = 313347,
+ doorOfShadows = 300728,
+ fleshcraft = 324631,
+ soulshape = 310143,
+ flicker = 324701,
+ summonSteward = 324739,
+ },
+ debuffs = {
+ bloodOfTheEnemy = 297108,
+ concentratedFlame = 295368,
+ conductiveInk = 302565,
+ dampening = 110310,
+ eyeOfLeotheras = 206649,
+ necroticWound = 209858,
+ razorCoral = 303568, --304877,
+ repeatPerformance = 304409,
+ shiverVenom = 301624,
+ temptation = 234143,
+ eyeOfCorruption = 315161,
+ grandDelusions = 319695,
+ graspingTendrils = 315176,
+ creepingMadness = 313255,
+ fixate = 318078,
+ thirstForBlood = 266107,
+ mightyBash = 5211,
+ defiledGround = 314565,
+ vileCorruption = 314392,
+ timeToFeed = 162415, -- Oshir Iron Docks
+ cascadingTerror = 314478,
+ bwonsamdiWrath = 284663, -- Bwonsamdi's Wrath debuff
+ quaking = 240448, -- M+ Quaking affix
+ reaping = 288388, -- M+ Reaping affix
+ promiseOfPower = 282566, -- M+ Promise of Power affix
+ arcaneBurst = 303657, -- M+ Arcane Burst affix
+ },
+ essences = {
+ },
+ },
+ },
+}
\ No newline at end of file
diff --git a/System/Lists/Expansions/TBC/Spells.lua b/System/Lists/Expansions/TBC/Spells.lua
new file mode 100644
index 0000000000..add715e471
--- /dev/null
+++ b/System/Lists/Expansions/TBC/Spells.lua
@@ -0,0 +1,3964 @@
+local _, br = ...
+if br.lists == nil then br.lists = {} end
+
+-- Racial spell IDs for The Burning Crusade (includes Blood Elves and Draenei).
+br.lists.racials = {
+ -- Alliance
+ Dwarf = 20594, -- Stoneform
+ Gnome = 20589, -- Escape Artist
+ Human = 59752, -- Every Man for Himself
+ NightElf = 20580, -- Shadowmeld
+ -- Horde
+ Tauren = 20549, -- War Stomp
+ Troll = 26297, -- Berserking
+ Scourge = 7744, -- Will of the Forsaken
+ -- Dynamic lookup base IDs
+ _bloodElfBase = 69179,
+ _draeneiBase = 28880,
+ _orcBase = 33702,
+}
+
+br.lists.spells = {
+ DRUID = {
+ -- Initial
+ [1447] = {
+
+ },
+ -- Balance
+ [102] = {
+ abilities = {
+ celestialAlignment = 194223,
+ fullMoon = 274283,
+ furyOfElune = 202770,
+ halfMoon = 274282,
+ innervate = 29166,
+ starfire = 194153,
+ newMoon = 274281,
+ removeCorruption = 2782,
+ solarBeam = 78675,
+ typhoon = 132469,
+ wrath = 190984,
+ starfall = 191034,
+ starsurge = 78674,
+ sunfire = 93402,
+ stellarFlare = 202347,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ blessingOfElune = 202737,
+ blessingOfAnshe = 202739,
+ incarnationChoseOfElune = 102560,
+ celestialAlignment = 194223,
+ eclipse_lunar = 48518,
+ eclipse_solar = 48517,
+ onethsOverconfidence = 209407,
+ onethsIntuition = 209406,
+ solarEmpowerment = 164545,
+ innervate = 29166,
+ lunarEmpowerment = 164547,
+ warriorOfElune = 202425,
+ balanceForm = 24858,
+ stellarDrift = 202461,
+ emeraldDreamcatcher = 208190,
+ owlkinFrenzy = 157228,
+ powerOfEluneTheMoonGoddness = 208284,
+ sephuzSecret = 208052,
+ astralAcceleration = 242232,
+ newMoonController = -2027671,
+ solstice = 343648,
+ starfall = 191034,
+ starLord = 279709, --backwards compatible
+ starlord = 279709,
+ livelySpirit = 279279,
+ arcanicPulsar = 287790,
+ primordialArcanicPulsar = 338668,
+ balanceOfAllThings = 339942,
+ balanceOfAllThingsNature = 339943, -- Nature Buff
+ balanceOfAllThingsArcane = 339946, -- Arcane Buff
+ timewornDreambinder = 339949,
+ kindredEmpowerment = 327022,
+ onethsClearVision = 338661,
+ ravenousFrenzy = 323546,
+
+ },
+ conduits = {
+ preciseAlignment = 340706,
+
+
+ },
+ covenants = {
+ loneEmpowerment = 338142,
+ ravenousFrenzy = 323546,
+ empowerBond = 326446,
+ },
+ debuffs = {
+ stellarFlare = 202347,
+ moonfire = 164812,
+ sunfire = 164815,
+ stellarEmpowerment = 197637,
+ },
+ glyphs = {
+ },
+ runeforges = {
+ balanceOfAllThings = 339942,
+ lycarasFleetingGlimpse = 340059,
+ primordialArcanicPulsar = 338668,
+ timewornDreambinder = 339949,
+
+ },
+ talents = {
+ aetherialKindling = 327541,
+ astralCommunion = 202359,
+ astralSmolder = 394058,
+ balanceOfAllThings = 394049,
+ bloomingInfusion = 429433,
+ bounteousBloom = 429215,
+ celestialAlignment = 194223,
+ cenariusMight = 455797,
+ controlOfTheDream = 434249,
+ convokeTheSpirits = 323764,
+ denizenOfTheDream = 394076,
+ dreamSurge = 433831,
+ durabilityOfNature = 429227,
+ earlySpring = 428937,
+ eclipse = 79577,
+ elunesGuidance = 393991,
+ expansiveness = 429399,
+ forceOfNature = 205636,
+ furyOfElune = 202770,
+ grovesInspiration = 429402,
+ harmonyOfTheGrove = 428731,
+ incarnationChosenOfElune = 394013,
+ lightOfTheSun = 202918,
+ lunarShrapnel = 393868,
+ naturesBalance = 202430,
+ naturesGrace = 393959,
+ newMoon = 274281,
+ orbitBreaker = 383197,
+ potentEnchantments = 429420,
+ powerOfGoldrinn = 394046,
+ powerOfNature = 428859,
+ powerOfTheDream = 434220,
+ protectiveGrowth = 433748,
+ radiantMoonlight = 394121,
+ rattleTheStars = 393954,
+ shootingStars = 202342,
+ solarBeam = 78675,
+ solstice = 343647,
+ soulOfTheForest = 114107,
+ starfall = 191034,
+ starlord = 202345,
+ starweaver = 393940,
+ stellarFlare = 202347,
+ sunderedFirmament = 294108,
+ treantsOfTheMoon = 428544,
+ twinMoons = 279620,
+ umbralEmbrace = 393760,
+ umbralIntensity = 383195,
+ waningTwilight = 393956,
+ warriorOfElune = 202425,
+ wildMushroom = 88747,
+ },
+ traits = {
+ arcanicPulsar = 287773,
+ dawningSun = 276152,
+ highNoon = 278505,
+ lunarShrapnel = 278507,
+ powerOfTheMoon = 273367,
+ streakingStars = 272871,
+ sunblaze = 274397,
+ }
+ },
+ -- Feral
+ [103] = {
+ abilities = {
+ -- -- adaptiveSwarmDamage = 391889, -- Not Castable but needed for In-Flight
+ -- -- adaptiveSwarmHeal = 391891, -- Not Castable but needed for In-Flight
+ -- berserk = 106951,
+ -- maim = 22570,
+ -- -- moonfireCat = 155625,
+ -- naturesSwiftness = 132158,
+ -- pounce = 9005,
+ -- ravage = 6785,
+ -- removeCorruption = 2782,
+ -- rip = 1079,
+ -- savageRoar = 52610,
+ -- shred = 5221,
+ -- skullBash = 106839,
+ -- survivalInstincts = 61336,
+ -- swipe = 62078,
+ -- thrash = 106830,
+ },
+ buffs = {
+ -- berserk = 106952,
+ -- clearcasting = 135700,
+ -- dreamOfCenarius = 145152,
+ -- feralFury = 144865,
+ -- feralRage = 146874,
+ -- incarnation = 102543,
+ -- naturesSwiftness = 132158,
+ -- predatorySwiftness = 69369,
+ -- savageRoar = 52610,
+ -- survivalInstincts = 61336,
+ },
+ debuffs = {
+ -- pounce = 9005,
+ -- rip = 1079,
+ -- thrash = 106830,
+ },
+ items = {
+ },
+ talents = {
+ -- cenarionWard = 102351,
+ -- disorientingRoar = 99,
+ -- displacerBeast = 102280,
+ -- dreamOfCenarius = 108373,
+ -- faerieSwarm = 106707,
+ -- felineSwiftness = 131768,
+ -- forceOfNature = 106737,
+ -- heartOfTheWild = 108288,
+ -- incarnation = 106731,
+ -- massEntanglement = 102359,
+ -- mightyBash = 5211,
+ -- naturesVigil = 124974,
+ -- renewal = 108238,
+ -- soulOfTheForest = 114107,
+ -- typhoon = 132469,
+ -- ursolsVortex = 102793,
+ -- wildCharge = 102401,
+ -- yserasGift = 145108,
+ },
+ },
+ -- Guardian
+ [104] = {
+ abilities = {
+ berserk = 50334,
+ incapacitatingRoar = 99,
+ incarnationGuardianOfUrsoc = 102558,
+ maul = 6807,
+ removeCorruption = 2782,
+ skullBash = 106839,
+ stampedingRoar = 106898,
+ survivalInstincts = 61336,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ berserk = 50334,
+ galacticGuardian = 213708,
+ goryFur = 201671,
+ incarnationGuardianOfUrsoc = 102558,
+ gore = 93622,
+ pulverize = 158792,
+ savageCombatant = 340613,
+ survivalInstincts = 61336,
+ toothAndClaw = 135286,
+ },
+ conduits = {
+ savageCombatant = 340609,
+
+ },
+ covenants = {
+ loneProtection = 338018,
+ },
+ debuffs = {
+ moonfireGuardian = 164812,
+ toothAndClaw = 135601,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ afterTheWildfire = 371905,
+ aggravateWounds = 441829,
+ berserkPersistence = 377779,
+ berserkRavage = 343240,
+ berserkUncheckedAggression = 377623,
+ bestialStrength = 441841,
+ bloodFrenzy = 203962,
+ brambles = 203953,
+ bristlingFur = 155835,
+ circleOfLifeAndDeath = 391969,
+ clawRampage = 441835,
+ convokeTheSpirits = 323764,
+ dreadfulWound = 441809,
+ dreamOfCenarius = 372119,
+ earthwarden = 203974,
+ elunesFavored = 370586,
+ empoweredShapeshifting = 441689,
+ flashingClaws = 393427,
+ fountOfStrength = 441675,
+ -- frontOfThePack = 377835,
+ furyOfNature = 370695,
+ galacticGuardian = 203964,
+ gore = 210706,
+ goryFur = 200854,
+ guardianOfElune = 213680,
+ improvedSurvivalInstincts = 328767,
+ incarnationGuardianOfUrsoc = 102558,
+ infectedWounds = 345208,
+ innateResolve = 377811,
+ killingStrikes = 441824,
+ layeredMane = 279552,
+ mangle = 231064,
+ maul = 6807,
+ packsEndurance = 441844,
+ pulverize = 80313,
+ rageOfTheSleeper = 200851,
+ ravage = 441583,
+ reinforcedFur = 393618,
+ reinvigoration = 372945,
+ rendAndTear = 204053,
+ ruthlessAggression = 441814,
+ scintillatingMoonlight = 238049,
+ soulOfTheForest = 158477,
+ strikeForTheHeart = 441845,
+ survivalInstincts = 61336,
+ survivalOfTheFittest = 203965,
+ tearDownTheMighty = 441846,
+ toothAndClaw = 135288,
+ twinMoonfire = 372567,
+ untamedSavagery = 372943,
+ -- ursineAdept = 300346,
+ ursocsEndurance = 393611,
+ ursocsFury = 372505,
+ ursocsGuidance = 393414,
+ viciousCycle = 371999,
+ vulnerableFlesh = 372618,
+ wildpowerSurge = 441691,
+ wildshapeMastery = 441678,
+ },
+ traits = {
+ layeredMane = 279552,
+ twistedClaws = 275906,
+ },
+ },
+ -- Restoration
+ [105] = {
+ abilities = {
+ efflorescence = 145205,
+ innervate = 29166,
+ ironbark = 102342,
+ lifebloom = 33763,
+ naturesCure = 88423,
+ nourish = 50464,
+ revitalize = 212040,
+ wrath = 5176,
+ sunfire = 93402,
+ tranquility = 740,
+ typhoon = 132469,
+ yserasGift = 145108,
+ naturesSwiftness = 132158,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ abundance = 207640,
+ cenarionWard = 102352,
+ clearcasting = 16870,
+ incarnationTreeOfLife = 33891,
+ innervate = 29166,
+ groveTending = 279793,
+ rejuvenationGermination = 155777,
+ lifebloom = 33763,
+ eclipse_lunar = 48518,
+ eclipse_solar = 48517,
+ --lunarEmpowerment = 164547,
+ regrowth = 8936,
+ --solarEmpowerment = 164545,
+ soulOfTheForest = 114108,
+ cultivat = 200389,
+ tranquility = 157982,
+ springblossom = 207386,
+ symbolOfHope = 64901,
+ fullbloom = 290213,
+ heartOfTheWild = 108291,
+ },
+ conduits = {
+ loneMeditation = 338035,
+ },
+ debuffs = {
+ moonfire = 164812,
+ sunfire = 164815,
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+ theDarkTitansLesson = 338831,
+ verdantInfusion = 338829,
+
+ },
+ talents = {
+ abundance = 207383,
+ balanceAffinity = 197632,
+ bloomingInfusion = 429433,
+ bondWithNature = 439929,
+ bounteousBloom = 429215,
+ buddingLeaves = 392167,
+ burstingGrowth = 440120,
+ callOfTheElderDruid = 426784,
+ cenarionWard = 102351,
+ cenariusGuidance = 393371,
+ cenariusMight = 455797,
+ controlOfTheDream = 434249,
+ cultivation = 200390,
+ dreamSurge = 433831,
+ durabilityOfNature = 429227,
+ earlySpring = 428937,
+ entanglingVortex = 439895,
+ expansiveness = 429399,
+ feralAffinity = 197490,
+ flourish = 197721,
+ flowerWalk = 439901,
+ germination = 155675,
+ grovesInspiration = 429402,
+ guardianAffinity = 197491,
+ harmoniousConstitution = 440116,
+ harmonyOfTheGrove = 428731,
+ huntBeneathTheOpenSkies = 439868,
+ implant = 440118,
+ incarnationTreeOfLife = 33891,
+ innerPeace = 197073,
+ lethalPreservation = 455461,
+ nourish = 50464,
+ overgrowth = 203651,
+ photosynthesis = 274902,
+ potentEnchantments = 429420,
+ powerOfNature = 428859,
+ powerOfTheDream = 434220,
+ protectiveGrowth = 433748,
+ resilientFlourishing = 439880,
+ rootNetwork = 439882,
+ soulOfTheForest = 158478,
+ springBlossoms = 207385,
+ strategicInfusion = 439890,
+ thrivingGrowth = 439528,
+ treantsOfTheMoon = 428544,
+ twinSprouts = 440117,
+ vigorousCreepers = 440119,
+ wildstalkersPower = 439926,
+ },
+ traits = {
+ dawningSun = 276152,
+ highNoon = 278505,
+ }
+ },
+ -- All
+ Shared = {
+ abilities = {
+ -- Bear Form
+ challengingRoar = 5209,
+ bash = {5211, 6798, 8983}, -- Ranks 1-3
+ demoralizingRoar = {99, 1735, 9490, 9747, 9898, 26998}, -- Ranks 1-6
+ enrage = 5229,
+ frenziedRegeneration = {22842, 22895, 22896, 26999}, -- Ranks 1-4
+ growl = 6795,
+ lacerate = 33745,
+ mangleBear = {33878, 33986, 33987}, -- Ranks 1-3
+ maul = {6807, 6808, 6809, 8972, 9745, 9880, 9881, 26996}, -- Ranks 1-8
+ swipe = {779, 780, 769, 9754, 9908, 26997}, -- Ranks 1-6
+ -- Cat Form
+ claw = {1082, 3029, 5201, 9849, 9850, 27000}, -- Ranks 1-6
+ cower = {8998, 9000, 9892, 31709, 27004}, -- Ranks 1-5
+ dash = {1850, 9821, 33357}, -- Ranks 1-3
+ faerieFireFeral = 16857,
+ ferociousBite = {22568, 22827, 22828, 22829, 31018, 24248}, -- Ranks 1-6
+ maim = 22570,
+ mangleCat = {33876, 33982, 33983}, -- Ranks 1-3
+ pounce = {9005, 9823, 9827, 27006}, -- Ranks 1-4
+ prowl = {5215, 6783, 9913}, -- Ranks 1-3
+ rake = {1822, 1823, 1824, 9904, 9845}, -- Ranks 1-5
+ ravage = {6785, 6787, 9866, 9867, 27005}, -- Ranks 1-5
+ rip = {1079, 9492, 9493, 9752, 9894, 9896, 27008}, -- Ranks 1-7
+ shred = {5221, 6800, 8992, 9829, 9830, 27001, 27002}, -- Ranks 1-7
+ tigersFury = {5217, 6793, 9845, 9846}, -- Ranks 1-4
+ -- Caster Form
+ barkskin = 22812,
+ entanglingRoots = 339,
+ faerieFire = 770,
+ healingTouch = {5185, 5186, 5187, 5188, 5189, 6778, 8903, 9758, 9888, 9889}, -- Ranks 1-10
+ innervate = 29166,
+ markOfTheWild = {1126, 5232, 6756, 5234, 8907, 9884, 9885}, -- Ranks 1-7
+ moonfire = {8921, 8924, 8925, 8926, 8927, 8928, 8929, 9833, 9834, 9835}, -- Ranks 1-10
+ naturesGrasp = 16689,
+ omenOfClarity = 16864,
+ regrowth = {8936, 8938, 8939, 8940, 8941, 9750, 9856, 9857, 9858}, -- Ranks 1-9
+ rejuvenation = {774, 1058, 1430, 2090, 2091, 3627, 8910, 9839, 9840, 9841}, -- Ranks 1-10
+ starfire = {2912, 8949, 8950, 8951, 9875, 9876}, -- Ranks 1-6
+ thorns = {467, 782, 1075, 8914, 9756, 9910}, -- Ranks 1-6
+ wrath = {5176, 5177, 5178, 5179, 5180, 6780, 8905, 9912}, -- Ranks 1-8
+ -- Dispels
+ abolishPoison = 2893,
+ curePoison = 8946,
+ removeCurse = 2782,
+ -- General
+ autoAttack = 6603,
+ -- Shapeshifting
+ aquaticForm = 1066,
+ bearForm = 5487,
+ catForm = 768,
+ direBearForm = 9634,
+ moonkinForm = 24858,
+ flightForm = 33943,
+ swiftFlightForm = 40120,
+ travelForm = 783,
+ },
+ animas = {
+ -- lycarasBargin = 329960,
+ -- lycarasTwig = 330666,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ -- Buffs
+ abolishPoison = 2893,
+ barkskin = 22812,
+ clearcasting = 16870,
+ enrage = 5229,
+ giftOfTheWild = {21849, 21850, 26991},
+ markOfTheWild = {1126, 5232, 6756, 5234, 8907, 9884, 9885},
+ naturesGrasp = 16689,
+ omenOfClarity = 16864,
+ prowl = 5215,
+ regrowth = {8936, 8938, 8939, 8940, 8941, 9750, 9856, 9857, 9858},
+ rejuvenation = {774, 1058, 1430, 2090, 2091, 3627, 8910, 9839, 9840, 9841},
+ shadowmeld = 20580,
+ thorns = {467, 782, 1075, 8914, 9756, 9910},
+ tigersFury = 5217,
+ -- Forms
+ aquaticForm = 1066,
+ bearForm = 5487,
+ catForm = 768,
+ direBearForm = 9634,
+ flightForm = 33943,
+ moonkinForm = 24858,
+ swiftFlightForm = 40120,
+ travelForm = 783,
+ },
+ conduits = {
+ -- deepAllegiance = 341378,
+ -- tasteForBlood = 340682,
+ },
+ covenants = {
+ -- adaptiveSwarm = 325727,
+ -- adaptiveSwarmHeal = 325748,
+ -- convokeTheSpiritsCovenant = 323764,
+ -- kindredSpirits = 326434,
+ -- ravenousFrenzy = 323546,
+ },
+ debuffs = {
+ demoralizingRoar = 99,
+ entanglingRoots = 339,
+ faerieFire = {770, 778, 9749, 9907},
+ faerieFireFeral = {16857, 17390, 17391, 17392},
+ mangleBear = {33878, 33986, 33987},
+ mangleCat = {33876, 33982, 33983},
+ moonfire = {8921, 8924, 8925, 8926, 8927, 8928, 8929, 9833, 9834, 9835},
+ rake = {1822, 1823, 1824, 9904},
+ rip = {1079, 9492, 9493, 9752, 9894, 9896},
+ },
+ glyphs = {
+ -- glyphOfTheCheetah = 131113,
+ -- glyphOfTheDoe = 224122,
+ -- glyphOfTheFeralChameleon = 210333,
+ -- glyphOfTheOrca = 114333,
+ -- glyphOfTheSentinel = 219062,
+ -- glyphOfTheUrsolChameleon = 107059,
+ },
+ runeforges = {
+ -- apexPredatorsCraving = 339139,
+ -- eyeOfFearfullSymmetry = 339141,
+ -- frenzyband = 340053,
+ },
+ talents = {
+ },
+ talentsHeroic = {
+ -- bloomingInfusion = 429433,
+ -- bounteousBloom = 429215,
+ -- burstingGrowth = 440120,
+ -- cenariusMight = 455797,
+ -- controlOfTheDream = 434249,
+ -- dreamSurge = 433831,
+ -- durabilityOfNature = 429227,
+ -- earlySpring = 428937,
+ -- expansiveness = 429399,
+ -- grovesInspiration = 429402,
+ -- harmonyOfTheGrove = 428731,
+ -- potentEnchantments = 429420,
+ -- powerOfNature = 428859,
+ -- powerOfTheDream = 434220,
+ -- protectiveGrowth = 433748,
+ -- resilientFlourishing = 439880,
+ -- rootNetwork = 439882,
+ -- treantsOfTheMoon = 428544,
+ },
+ traits = {
+ -- livelySpirit = 279642,
+ },
+ },
+ },
+ HUNTER = {
+ -- BeastMastery
+ [253] = {
+ abilities = {
+ aspectOfTheWild = 193530,
+ barbedShot = 217200,
+ bestialWrath = 19574,
+ cobraShot = 193455,
+ concussiveShot = 5116,
+ counterShot = 147362,
+ intimidation = 19577,
+ killCommand = 34026,
+ killShot = 53351,
+ multishot = 2643,
+ },
+ animas = {
+ piercingScope = 331183,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ aspectOfTheWild = 193530,
+ barbedShot = 217200,
+ beastCleave = 118455,
+ berserking = 26297,
+ bestialWrath = 19574,
+ danceOfDeath = 274443,
+ direBeast = 120694,
+ frenzy = 272790,
+ parselsTongue = 248084,
+ spittingCobra = 194407,
+ volley = 194386,
+ },
+ debuffs = {
+ barbedShot = 217200,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ animalCompanion = 267116,
+ barrage = 120360,
+ bloodshed = 321530,
+ direBeast = 120679,
+ killCleave = 378207,
+ killerCobra = 199532,
+ killerInstinct = 273887,
+ oneWithThePack = 199528,
+ scentOfBlood = 193532,
+ stampede = 201430,
+ stomp = 199530,
+ thrillOfTheHunt = 257944,
+ wailingArrow = 392060,
+ },
+ traits = {
+ danceOfDeath = 274441,
+ primalInstincts = 279806,
+ rapidReload = 278530,
+ },
+ },
+ -- Marksmanship
+ [254] = {
+ abilities = {
+ },
+ artifacts = {
+ },
+ buffs = {
+ bulletstorm = 389020,
+ bullseye = 204090,
+ deadEye = 321461,
+ deathblow = 378770,
+ feignDeath = 5384,
+ inTheRhythm = 272733,
+ lethalShots = 260395,
+ lockAndLoad = 194594,
+ masterMarksman = 269576,
+ preciseShots = 260242,
+ razorFragments = 388993,
+ salvo = 400456,
+ steadyFocus = 193534,
+ trickShots = 257622,
+ trueshot = 288613,
+ unerringVision = 274447,
+ },
+ debuffs = {
+ huntersMark = 257284,
+ serpentSting = 271788,
+ },
+ glyphs = {
+
+ },
+ interrupts = {
+ counterShot = 147362,
+ },
+ talents = {
+ aimedShot = 19434,
+ barrage = 120360,
+ bindingShackles = 321468,
+ bulletstorm = 389019,
+ bullseye = 204089,
+ callingTheShots = 260404,
+ carefulAim = 260228,
+ chimaeraShot = 342049,
+ crackShot = 321293,
+ deathblow = 378769,
+ eagletalonsTrueFocus = 389449,
+ explosiveShot = 212431,
+ fanTheHammer = 459794,
+ focusedAim = 378767,
+ heavyAmmo = 378910,
+ hydrasBite = 260241,
+ improvedSteadyShot = 321018,
+ inTheRhythm = 407404,
+ killShot = 53351,
+ killZone = 459921,
+ killerAccuracy = 378765,
+ legacyOfTheWindrunners = 406425,
+ lightAmmo = 378913,
+ lockAndLoad = 194595,
+ loneWolf = 155228,
+ masterMarksman = 260309,
+ multishot = 257620,
+ nightHunter = 378766,
+ penetratingShots = 459783,
+ preciseShots = 260240,
+ rapidFire = 257044,
+ rapidFireBarrage = 459800,
+ razorFragments = 384790,
+ readiness = 389865,
+ salvo = 400456,
+ serpentstalkersTrickery = 378888,
+ smallGameHunter = 459802,
+ steadyFocus = 193533,
+ streamline = 260367,
+ surgingShots = 391559,
+ tacticalReload = 400472,
+ trickShots = 257621,
+ trueshot = 288613,
+ unerringVision = 386878,
+ volley = 260243,
+ wailingArrow = 459806,
+ },
+ traits = {
+ focusedFire = 278531,
+ inTheRhythm = 264198,
+ rapidReload = 278530,
+ steadyAim = 277651,
+ surgingShots = 287707,
+ unerringVision = 274444,
+ },
+ },
+ -- Survival
+ [255] = {
+ abilities = {
+ aspectOfTheEagle = 186289,
+ carve = 187708,
+ coordinatedAssault = 266779,
+ harpoon = 190925,
+ intimidation = 19577,
+ killCommand = 259489,
+ killShot = 320976,
+ muzzle = 187707,
+ pheromoneBomb = 270323, -- Wildfire Infusion
+ raptorStrike = 186270,
+ serpentSting = 259491,
+ shrapnelBomb = 270335, -- Wildfire Infusion
+ volatileBomb = 271045, -- Wildfire Infusion
+ wakeUp = 210000,
+ wildfireBomb = 259495,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ aspectOfTheEagle = 186289,
+ blurOfTalons = 277969,
+ coordinatedAssault = 360952,
+ exposedFlank = 252094, -- Tier 21
+ mongooseFury = 259388,
+ tipOfTheSpear = 260286,
+ vipersVenom = 268552,
+ madBombardier = 363805, -- Tier 28
+ },
+ debuffs = {
+ bloodseeker = 259277,
+ internalBleeding = 270343,
+ serpentSting = 259491,
+ shrapnelBomb = 270339,
+ wildfireBomb = 269747,
+ pheromoneBomb = 270332,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ alphaPredator = 269737,
+ birdsOfPrey = 260331,
+ bloodseeker = 260248,
+ butchery = 212436,
+ crackShot = 321293,
+ flankingStrike = 269751,
+ furyOfTheEagle = 203415,
+ guerrillaTactics = 264332,
+ hydrasBite = 260241,
+ mongooseBite = 259387,
+ steelTrap = 162488,
+ termsOfEngagement = 265895,
+ tipOfTheSpear = 260285,
+ vipersVenom = 268501,
+ wildfireInfusion = 271014,
+ },
+ traits = {
+ blurOfTalons = 277653,
+ latentPoison = 273283,
+ primevalIntuition = 288570,
+ upCloseAndPersonal = 278533,
+ venomousFangs = 274590,
+ wildernessSurvival = 279589,
+ }
+ },
+ -- All
+ Shared = {
+ abilities = {
+ -- Hunter Abilities
+ arcaneShot = 185358,
+ aspectOfTheChameleon = 61648,
+ aspectOfTheCheetah = 186257,
+ aspectOfTheTurtle = 186265,
+ disengage = 781,
+ eagleEye = 6197,
+ exhilaration = 109304,
+ eyeOfTheBeast = 321297,
+ feignDeath = 5384,
+ flare = 1543,
+ fortitudeOfTheBear = 392956,
+ freezingTrap = 187650,
+ huntersMark = 257284,
+ steadyShot = 56641,
+ trackBeasts = 1494,
+ trackDemons = 19878,
+ trackDragonkin = 19879,
+ trackElementals = 19880,
+ trackGiants = 19882,
+ trackHidden = 19885,
+ trackHumanoids = 19883,
+ trackMechanicals = 229533,
+ trackUndead = 19884,
+ wingClip = 195645,
+ -- Pet Management
+ beastLore = 1462,
+ callPet = 9,
+ callPet1 = 883,
+ callPet2 = 83242,
+ callPet3 = 83243,
+ callPet4 = 83244,
+ callPet5 = 83245,
+ commandPet = 272651,
+ dismissPet = 2641,
+ feedPet = 6991,
+ fetch = 125050,
+ mendPet = 136,
+ playDead = 209997,
+ revivePet = 982,
+ tameBeast = 1515,
+ wakeUp = 210000,
+ ----------------------------
+ -- Pet - Basic Abilities ---
+ ----------------------------
+ bite = 17253, -- Basalisk, Bat, Beetle, Blood Beast, Boar, Carapid, Carrion Bird, Chimera, Core Hound, Crocolisk, Devilsaur, Direhorn, Dragonhawk, Feathermane, Fox, Hound, Hydra, Hyena, Lizard, Mechanical, Pterrordax, Ravager, Ray, Riverbeast, Serpent, Shale Beast, Spider, Stone Hound, Turtle, Warp Stalker, Water Strider, Wind Serpent, Wolf, Worm
+ claw = 16827, -- Aqiri, Bear, Bird of Prey, Cat, Crab, Raptor, Rodent, Scorpid, Spirit Beast, Tallstrider
+ dash = 61684, -- Speed Increase (ALL PETS)
+ smack = 49966, -- Camel, Clefthoof, Courser, Crane, Gorilla, Gruffhorn, Mammoth, Monkey, Moth, Oxen, Scalehide, Sporebat, Stag, Toad, Wasp
+ growl = 2649, -- Taunt (ALL PETS)
+ ---------------------------
+ -- Pet - Spec Abilities ---
+ ---------------------------
+ primalRage = 264667, -- Ferocity Dmg Cd
+ -- survivalOfTheFittest = 264735, -- Tenacity Def Cd
+ mastersCall = 53271, -- Cunning Freedom
+ ------------------------------
+ -- Pet - Special Abilities ---
+ ------------------------------
+ -- AOE
+ burrowAttack = 93433, -- Worm
+ froststormBreath = 92380, -- Chimera - Channeled Cone AOE
+ -- Defense/Dodge
+ agileReflexes = 160011, -- Fox - Dodge
+ bristle = 263869, -- Boar - Defensive
+ bulwark = 279410, -- Carapid - Defensive
+ catlikeReflexes = 263892, -- Cat - Dodge
+ defenseMatrix = 263868, -- Mechanical - Defensive
+ dragonsGuile = 263887, -- Dragonhawk - Dodge
+ featherFlurry = 263916, -- Feathermane - Dodge
+ fleethoof = 341117, -- Courser - Dodge
+ hardenCarapace = 90339, -- Beetle - Defensive
+ obsidianSkin = 263867, -- Core Hound - Defensive
+ primalAgility = 160044, -- Monkey - Dodge
+ scaleShield = 263865, -- Scalehide - Defensive
+ serpentSwiftness = 263904, -- Serpent - Dodge
+ shellShield = 26064, -- Turtle - Defensive
+ solidShell = 160063, -- Shale Beast - Defensive
+ swarmOfFlies = 279336, -- Toad - Dodge
+ wingedAgility = 264360, -- Wind Serpent - Dodge
+ -- Dispel
+ chiJiTranq = 344350, -- Crane
+ naturesGrace = 344352, -- Stag
+ netherEnergy = 344349, -- Ray -- netherEnergy
+ serenityDust = 344353, -- Moth
+ sonicScreech = 344348, -- Bat -- sonicScreech
+ soothingWater = 344346, -- Water Strider
+ spiritPulse = 344351, -- Spirit Beast -- spiritPulse
+ sporeCloud = 344347, -- Sporebat
+ -- Heal
+ eternalGuardian = 267922, -- Stone Hound - Ressurect
+ feast = 159953, -- Devilsaur - Heal (req dead humanoid/beast nearby)
+ spiritmend = 90361, -- Spirit Beast - Heal
+ -- Mortal Wounds Debuff
+ acidBite = 263863, -- Hydra - Mortal Wounds Debuff
+ bloodyScreech = 24423, -- Carrion Bird - Mortal Wounds Debuff
+ deadlySting = 160060, -- Scorpid - Mortal Wounds Debuff
+ gnaw = 263856, -- Rodent - Mortal Wounds Debuff
+ gore = 263861, -- Direhorn - Mortal Wounds Debuff
+ grievousBite = 279362, -- Lizard - Mortal Wounds Debuff
+ gruesomeBite = 160018, -- Riverbeast - Mortal Wounds Debuff
+ infectedBite = 263853, -- Hyena - Mortal Wounds Debuff
+ monsterousBite = 54680, -- Devilsaur - Mortal Wounds Debuff
+ ravage = 263857, -- Ravager - Mortal Wounds Debuff
+ savageRend = 263854, -- Savage Rend - Mortal Wounds Debuff
+ toxicSting = 263858, -- Wasp - Mortal Wounds Debuff
+ -- Slow
+ acidSpit = 263446, -- Worm - Slow
+ ankleCrack = 50433, -- Crocolisk - Slow
+ bloodBolt = 288962, -- Blood Beast - Slow
+ dustCloud = 50285, -- Tallstrider - Slow
+ frostBreath = 54644, -- Chimera - Slow
+ furiousBite = 263840, -- Wolf - Slow
+ lockJaw = 263423, -- Hound - Slow
+ petrifyingGaze = 263841, -- Basilisk - Slow
+ pin = 50245, -- Crab - Slow
+ talonRend = 263852, -- Bird of Prey - Slow
+ tendonRip = 160065, -- Aqiri - Slow
+ trample = 341118, -- Mammoth - Slow
+ warpTime = 35346, -- Warp Stalker - Slow
+ webSpray = 160067, -- Spider - Slow
+ -- Slow Fall
+ updraft = 160007, -- Feathermane/Pterrordax - Slow Fall
+ -- Stealth
+ prowl = 24450, -- Stealth
+ spiritWalk = 90328, -- Spirit Beast - Stealth
+ -- Tricks / Play / Rest
+ play = 90347, -- Fox
+ restBear = 94019, -- Bear
+ restRodent = 126364, -- Rodent
+ trickCrane = 126259, -- Crane
+ trickBirdOfPrey = 94022, -- Bird of Prey
+ -- Water Walking
+ surfaceTrot = 126311, -- Water Strider - Water Walking
+ },
+ animas = {
+ soulforgeEmbers = 331197,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ aspectOfTheTurtle = 186265,
+ eagletalonsTrueFocus = 336851,
+ feignDeath = 5384,
+ flayersMark = 324156, -- Covenant
+ killingFrenzy = 363665, -- T28 4pc
+ mendPet = 136,
+ nesingwarysTrappingApparatus = 336744,
+ playDead = 209997,
+ prowl = 24450,
+ resonatingArrow = 356263, -- Covenant
+ spiritWalk = 90328,
+ surfaceTrot = 126311, -- Water Strider - Water Walking
+ updraft = 160007, -- Feathermane/Pterrordax - Slow Fall
+ volley = 260243,
+ wildSpirits = 328231,
+ },
+ conduits = {
+ bloodletting = 341440,
+ marksmansAdvantage = 339264,
+ },
+ covenants = {
+ flayedShot = 324149,
+ resonatingArrow = 308491,
+ wildSpirits = 328231,
+ },
+ debuffs = {
+ bestialFerocity = 191413,
+ freezingTrap = 3355,
+ huntersMark = 257284,
+ intimidation = 24934,
+ latentPoison = 378015,
+ mortalWounds = 115804, -- Pet Ability Debuff
+ resonatingArrow = 308498, -- Covenant
+ soulforgeEmbers = 331269, -- Covenant
+ tarTrap = 135299,
+ wildMark = 328275, -- Covenant
+ },
+ glyphs = {
+
+ },
+ interrupts = {
+ freezingTrap = 187650,
+ },
+ runeforges = {
+ bagOfMunitions = 356264,
+ eagletalonsTrueFocus = 336849,
+ nesingwarysTrappingApparatus = 336743,
+ pouchOfRazorFragments = 356618,
+ rylakstalkersConfoundingStrikes = 336901,
+ secretsOfTheUnblinkingVigil = 336878,
+ serpentstalkersTrickery = 336870,
+ soulforgeEmbers = 336745,
+ surgingShots = 336867,
+ qaplaEredunWarOrder = 336830,
+ },
+ talents = {
+ bindingShot = 109248,
+ blackrockMunitions = 462036,
+ bornToBeWild = 266921,
+ burstingShot = 186387,
+ camouflage = 199483,
+ concussiveShot = 5116,
+ counterShot = 147362,
+ devilsaurTranquilizer = 459991,
+ disruptiveRounds = 343244,
+ emergencySalve = 459517,
+ entrapment = 393344,
+ ghillieSuit = 459466,
+ highExplosiveTrap = 236776,
+ huntersAvoidance = 384799,
+ implosiveTrap = 462031,
+ improvedKillShot = 343248,
+ improvedTraps = 343247,
+ intimidation = 19577,
+ keenEyesight = 378004,
+ kindlingFlare = 459506,
+ kodoTranquilizer = 459983,
+ loneSurvivor = 388039,
+ misdirection = 34477,
+ momentOfOpportunity = 459488,
+ naturalMending = 270581,
+ noHardFeelings = 459546,
+ paddedArmor = 459450,
+ pathfinding = 378002,
+ posthaste = 109215,
+ quickLoad = 378771,
+ rejuvenatingWind = 385539,
+ roarOfSacrifice = 53480,
+ scareBeast = 1513,
+ scatterShot = 213691,
+ scoutsInstincts = 459455,
+ scrappy = 459533,
+ serratedTips = 459502,
+ survivalOfTheFittest = 264735,
+ specializedArsenal = 459542,
+ tarTrap = 187698,
+ tarcoatedBindings = 459460,
+ territorialInstincts = 459507,
+ trailblazer = 199921,
+ tranquilizingShot = 19801,
+ triggerFinger = 459534,
+ unnaturalCauses = 459527,
+ wildernessMedicine = 343242,
+ },
+ talentsHeroic = {
+ -- Dark Ranger
+ blackArrow = 430703,
+ darkChains = 430712,
+ darkEmpowerment = 430718,
+ deathShade = 430711,
+ darknessCalls = 430722,
+ embraceTheShadows = 430704,
+ graveReaper = 430719,
+ overshadow = 430716,
+ shadowErasure = 430720,
+ shadowHounds = 430707,
+ shadowLash = 430717,
+ shadowSurge = 430714,
+ smokeScreen = 430709,
+ witheringFire = 430715,
+ -- Pack Leader
+ beastOfOpportunity = 445700,
+ corneredPrey = 445702,
+ coveringFire = 445715,
+ cullTheHerd = 445717,
+ denRecovery = 445710,
+ frenziedTear = 445696,
+ furiousAssault = 445699,
+ howlOfThePack = 445707,
+ packAssault = 445721,
+ packCoordination = 445505,
+ scatteredPrey = 445768,
+ tirelessHunt = 445701,
+ viciousHunt = 445404,
+ wildAttacks = 445708,
+ -- Sentinel
+ catchOut = 451516,
+ crescentSteel = 451530,
+ extrapolatedShots = 450374,
+ eyesClosed = 450381,
+ dontLookBack = 450373,
+ invigoratingPulse = 450379,
+ lunarStorm = 450385,
+ overwatch = 450384,
+ releaseAndReload = 450376,
+ sentinel = 450369,
+ sentinelPrecision = 450375,
+ sentinelWatch = 451546,
+ sideline = 450378,
+ symphonicArsenal = 450383,
+ },
+ },
+ },
+ MAGE = {
+ -- Arcane
+ [62] = {
+ abilities = {
+ alterTime = 342245,
+ arcaneBarrage = 44425,
+ arcaneBlast = 30451,
+ arcaneFamiliar = 205022,
+ arcaneMissiles = 5143,
+ arcaneOrb = 153626,
+ arcanePower = 12042,
+ chargedUp = 205032,
+ conjuremanaGem = 759,
+ displacement = 212801,
+ erosion = 205039,
+ evocation = 12051,
+ -- fireblast = 519836,
+ greaterInvisibility = 110959,
+ markOfAluneth = 224968,
+ netherTempest = 114923,
+ presenceofMind = 205025,
+ prismaticBarrier = 235450,
+ runeofPower = 116011,
+ slow = 31589,
+ supernova = 157980,
+ touchOfTheMagi = 321507,
+ },
+ artifacts = {
+ aegwynnsAscendance = 187680,
+ aegwynnsFury = 187287,
+ aegwynnsInperative = 187264,
+ aegwynnsIntensity = 238054,
+ aegwynnsWrath = 187321,
+ alunethsAvarice = 238090,
+ ancientPower = 214917,
+ arcanePurification = 187313,
+ arcaneRebound = 188006,
+ blastingRod = 187258,
+ cracklingEnergy = 187310,
+ etherealSensitivity = 187276,
+ everywhereAtOnce = 187301,
+ forceBarrier = 210716,
+ intensityOfTheTirisgarde = 241121,
+ markOfAluneth = 224968,
+ mightOfTheGuardians = 187318,
+ ruleOfThrees = 215463,
+ sloooowDown = 210730,
+ timeAndSpace = 238126,
+ torrentialBarrage = 187304,
+ touchOfTheMagi = 210725,
+ },
+ buffs = {
+ arcaneCharge = 36032,
+ arcaneFamiliar = 210126,
+ arcanePower = 12042,
+ -- arcaneCharge = 36032,
+ evocation = 12051,
+ expandedPotential = 327495,
+ arcaneMissles = 79683,
+ presenceOfMind = 205025,
+ prismaticBarrier = 235450,
+ rhoninsAssaultingArmwraps = 208081,
+ runeofPower = 116011,
+ ruleOfThrees = 264774,
+ brainStorm = 273330,
+ clearcasting = 263725,
+ },
+ debuffs = {
+ arcaneCharge = 36032,
+ netherTempest = 114923,
+ touchoftheMagi = 210824,
+
+
+ },
+ glyphs = {
+
+ },
+ talents = {
+ arcaneEcho = 342231,
+ amplification = 236628,
+ arcaneFamiliar = 205022,
+ arcaneOrb = 153626,
+ chronoShift = 235711,
+ mastersOfTime = 342249,
+ --erosion = 205039,
+ netherTempest = 114923,
+ overpowered = 155147,
+ resonance = 205028,
+ reverberate = 281482,
+ rulesOfThrees = 264354,
+ slipstream = 236457,
+ supernova = 157980,
+ timeAnomaly = 210805
+ --temporalFlux = 234302,
+ --wordsOfPower = 205035,
+ },
+ traits = {
+ anomalousImpact = 279867,
+ arcanePressure = 274594,
+ arcanePummeling = 270669,
+ brainStorm = 273326,
+ explosiveEcho = 278537,
+ galvanizingSpark = 278536,
+ },
+ },
+ -- Fire
+ [63] = {
+ abilities = {
+ alterTime = 108978,
+ blazingBarrier = 235313,
+ blastWave = 157981,
+ cinderstorm = 198929,
+ combustion = 190319,
+ dragonsBreath = 31661,
+ disciplinaryCommand = 327365,
+ fireball = 133,
+ fireBlast = 108853,
+ fireBlast2 = 319836,
+ flameOn = 205029,
+ flamestrike = 2120,
+ infernalCascade = 336821,
+ livingBomb = 44457,
+ meteor = 153561,
+ phoenixFlames = 257541,
+ pyroblast = 11366,
+ shiftingPower = 314791,
+ scorch = 2948,
+ soulIgnition = 345211,
+ },
+ artifacts = {
+ aftershocks = 194431,
+ phoenixReborn = 215773,
+ },
+ buffs = {
+ blazingBarrier = 235313,
+ blasterMaster = 274598,
+ combustion = 190319,
+ disciplinaryArcane = 327369,
+ disciplinaryCommand = 327365,
+ disciplinaryCommandFrost = 327366,
+ disciplinaryCommandFire = 327368,
+ firestorm = 333097,
+ heatingUp = 48107,
+ hotStreak = 48108,
+ iceFloes = 108839,
+ infernalCascade = 336832,
+ kaelthasUltimateAbility = 209455,
+ preheat = 273333,
+ pyroclasm = 269651,
+ shiftingPower = 314791,
+ sunKingsBlessings = 333313,
+ soulIgnition = 345211,
+ },
+ debuffs = {
+ ignite = 12654,
+ meteorBurn = 155158,
+ cauterized = 87024,
+
+ },
+ glyphs = {
+
+ },
+ talents = {
+ alexstraszasFury = 235870,
+ blastWave = 157981,
+ blazingSoul = 235365,
+ --cinderstorm = 198929,
+ conflagration = 205023,
+ --controlledBurn = 205033,
+ freneticSpeed = 236058,
+ firestarter = 205026,
+ flameOn = 205029,
+ flamePatch = 205037,
+ kindling = 155148,
+ livingBomb = 44457,
+ meteor = 153561,
+ focusMagic = 321358,
+ fromTheAshes = 342344,
+ pyromaniac = 205020,
+ pyroclasm = 269650,
+ searingTouch = 269644,
+ },
+ covenants = {
+ shiftingPower = 314791,
+ },
+ traits = {
+ preheat = 273332,
+ blasterMaster = 274596,
+ },
+ conduits = {
+ infernalCascade = 336821,
+ },
+ runeforges = {
+ firestorm = 333097,
+ grislyIcicle = 333393,
+ disciplinaryCommand = 327365,
+ sunKingsBlessings = 333313
+ }
+ },
+ -- Frost
+ [64] = {
+ abilities = {
+ alterTime = 108978,
+ blizzard = 190356,
+ coldSnap = 235219,
+ coneOfCold = 120,
+ --fireBlast = 108853, - Fire Mage version, Frost should not have this and already listed fireBlast below
+ flurry = 44614,
+ freeze = 231596,
+ frostbolt = 116,
+ frostBomb = 112948,
+ frozenOrb = 84714,
+ frozenTouch = 205030,
+ fireBlast = 319836,
+ iceBarrier = 11426,
+ iceFloes = 108839,
+ iceForm = 198144,
+ iceLance = 30455,
+ iceNova = 157997,
+ icyVeins = 12472,
+ petFreeze = 33395,
+ rayOfFrost = 205021,
+ runeofPower = 116011,
+ removeCurse = 475,
+ summonWaterElemental = 31687,
+ waterbolt = 31707,
+ },
+ artifacts = {
+ -- icyHand = 220817,
+ },
+ buffs = {
+ brainFreeze = 190446,
+ concentratedCoolness = 198148,
+ freezingRain = 270232,
+ frostBomb = 112948,
+ fingersOfFrost = 44544,
+ icyVeins = 12472,
+ chainReaction = 195418,
+ zannesuJourney = 226852,
+ timeWarp = 80353,
+ iceFloes = 108839,
+ iceForm = 198144,
+ iceBarrier = 11426,
+ icicles = 205473,
+ frozenMass = 242253,
+ },
+ debuffs = {
+ chainsOfIce = 65173,
+ chilled = 205708,
+ frostBomb = 112948,
+ frostNova = 122,
+ iceNova = 157997,
+ wintersChill = 228358,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ --articGale = 205038,
+ boneChilling = 205027,
+ cometStorm = 153595,
+ chainReaction = 278309,
+ ebonbolt = 257537,
+ --frostBomb = 112948,
+ frozenTouch = 205030,
+ frigidWinds = 235224,
+ freezingRain = 270233,
+ focusMagic = 321358,
+ glacialSpike = 199786,
+ glacialInsulation = 235297,
+ iceNova = 157997,
+ iceFloes = 108839,
+ lonelyWinter = 205024,
+ rayOfFrost = 205021,
+ splittingIce = 56377,
+ thermalVoid = 155149,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ alterTime = 108978,
+ arcaneBrilliance = 1459,
+ arcaneExplosion = 1449,
+ blink = 1953,
+ blizzard = 10,
+ coneOfCold = 120,
+ conjureManaGem = 759,
+ conjureRefreshment = 42955,
+ conjureRefreshmentTable = 43987,
+ counterspell = 2139,
+ deepFreeze = 44572,
+ evocation = 12051,
+ fireBlast = 2136,
+ flamestrike = 2120,
+ frostArmor = 7302,
+ frostfireBolt = 44614,
+ frostNova = 122,
+ iceBlock = 45438,
+ iceLance = 30455,
+ invisibility = 66,
+ manaBomb = 125430,
+ mageArmor = 6117,
+ mirrorImage = 55342,
+ moltenArmor = 30482,
+ polymorph = 118,
+ removeCurse = 475,
+ slowFall = 130,
+ spellsteal = 30449,
+ timeWarp = 80353,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ -- arcaneIntellect = 1459,
+ -- disciplinaryCommand = 327365,
+ -- expandedPotential = 327489,
+ -- focusMagic = 321358,
+ -- freezingWinds = 327364,
+ -- iceBlock = 45438,
+ -- incantersFlow = 1463,
+ -- slowFall = 130,
+ -- slickIce = 327509,
+ -- runeOfPower = 116014,
+ },
+ conduits = {
+
+ },
+ covenants = {
+ -- deathborne = 324220,
+ -- mirrorsOfTorment = 314793,
+ -- radiantSpark = 307443,
+ -- shiftingPower = 314791,
+ },
+ debuffs = {
+ -- frostNova = 122,
+ -- mirrorsOfTorment = 314793,
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+ -- disciplinaryCommand = 327365,
+ -- sunKingsBlessings = 333313,
+ -- grislyIcicle = 333393,
+ -- firestorm = 333097,
+
+ },
+ talents = {
+ -- incantersFlow = 1463,
+ -- iceWard = 205036,
+ -- focusMagic = 321358,
+ -- ringOfFrost = 113724,
+ -- runeOfPower = 116011,
+ -- shimmer = 212653,
+ --unstableMagic = 157976,
+ },
+ },
+ },
+ PALADIN = {
+ -- Holy
+ [65] = {
+ abilities = {
+ absolution = 212056,
+ auraMastery = 31821,
+ barrierOfFaith = 148039,
+ beaconOfFaith = 156910,
+ beaconOfLight = 53563,
+ beaconOfVirtue = 200025,
+ blessingOfAutumn = 388010,
+ blessingOfSpring = 388013,
+ blessingOfSummer = 388007,
+ blessingOfWinter = 388011,
+ cleanse = 4987,
+ divineProtection = 498,
+ holyLight = 82326,
+ holyShock = 20473,
+ lightOfDawn = 85222,
+ lightOfTheMartyr = 183998,
+ --lightsHammer = 114158,
+
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ --sealOfClarity = 384810,
+ avengingCrusader = 216331,
+ auraMastery = 31821,
+ avengingWrath = 31884,
+ avengingWrathCrit = 294027,
+ afterimage = 400745,
+ beaconOfLight = 53563,
+ beaconOfFaith = 156910,
+ blessingOfSacrifice = 6940,
+ beaconOfVirtue = 200025,
+ --bestowFaith = 223306,
+ --glimmerOfLight = 287280,
+ blessingOfDawn = 337747,
+ blessingOfDusk = 385126,
+ divineProtection = 498,
+ divinePurpose = 223819,
+ --ferventMartyr = 223316,
+ infusionOfLight = 54149,
+ innervate = 29166,
+ --ruleOfLaw = 214202,
+ liberation = 461471,
+ --theLightSaves = 200423,
+ vindicator = 200376,
+ symbolOfHope = 64901,
+ divineSteed = 254474,
+ --maraadsBreath = 340459,
+ unendingLight = 394709,
+ untemperedDedication = 339990,
+ veneration = 392939,
+ },
+ debuffs = {
+ judgement = 214222,
+ judgmentOfLight = 196941,
+ --glimmerOfLight = 287280,
+ consecration = 204242,
+ dawnWillCome = 364851,
+
+ },
+ glyphs = {
+
+ },
+ talents = {
+ avengingCrusader = 216331,
+ --awakening = 248033,
+ beaconOfFaith = 156910,
+ beaconOfVirtue = 200025,
+ --bestowFaith = 223306,
+ blessingOfSummer = 388007,
+ breakingDawn = 387879,
+ crusadersMight = 196926,
+ --glimmerOfLight = 325966,
+ holyPrism = 114165,
+ judgmentOfLight = 183778,
+ --lightsHammer = 114158,
+ --ruleOfLaw = 214202,
+ --sanctifiedWrath = 53376,
+ savedByTheLight = 157047,
+ divineToll = 375576,
+ },
+ traits = {
+ graceoftheJusticar = 278593,
+ indomitableJustice = 275496,
+ },
+ runeforges = {
+ shadowbreaker = 337812,
+ shockBarrier = 337825,
+ maraadsDyingBreath = 340458,
+ }
+ },
+ -- Protection
+ [66] = {
+ abilities = {
+ ardentDefender = 31850,
+ avengersShield = 31935,
+ blessedHammer = 229976,
+ cleanseToxins = 213644,
+ divineProtection = 498,
+ guardianOfAncientKings = 86659,
+ hammerOfTheRighteous = 53595,
+ judgment = 275779,
+ judgmentsOfTheWise = 105424,
+ rebuke = 96231,
+ righteousFury = 25780,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ ardentDefender = 31850,
+ avengingWrath = 31884,
+ blessingOfSpellwarding = 204018,
+ bulwarkOfOrder = 209388,
+ consecration = 188370,
+ divineShield = 642,
+ divinePurpose = 223819,
+ guardianOfAncientKings = 86659,
+ shieldOfTheRighteous = 132403,
+ shiningLight = 327510,
+ avengersValor = 197561,
+ divineSteed = 254474,
+ royalDecree = 340147,
+ },
+ debuffs = {
+ blessedHammer = 204301,
+ judgmentOfLight = 196941,
+ judgment = 197277,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ layOnHands = 633,
+ blessedHammer = 204019,
+ hammerOfTheRighteous = 53595,
+ innerLight = 386568,
+ redoubt = 280373,
+ holyShield = 152261,
+ grandCrusader = 85043,
+ -- shiningLight = 321136, -- No longer a talent?
+ consecratedGround = 204054,
+ improvedLayOnHands = 393027,
+ inspiringVanguard = 393022,
+ ardentDefender = 31850,
+ barricadeOfFaith = 385726,
+ consecrationInFlame = 379022,
+ improvedHolyShield = 393030,
+ sanctuary = 379021,
+ bulwarkOfOrder = 209389,
+ improvedArdentDefender = 393114,
+ blessingOfSpellwarding = 204018,
+ lightOfTheTitans = 378405,
+ strengthInAdversity = 393071,
+ crusadersResolve = 380188,
+ tyrsEnforcer = 378285,
+ relentlessInquisitor = 383388,
+ aveningWrathMight = 31884,
+ sentinel = 389539,
+ handOTheProtector = 315924,
+ strengthOfConviction = 379008,
+ resoluteDefender = 385422,
+ bastionOfLight = 378974,
+ guardianOfAncientKings = 86659,
+ crusadersJudgement = 204023,
+ uthersCounsel = 378425,
+ focusedEnmity = 378845,
+ soaringShield = 378457,
+ giftOfTheGoldenValkyr = 378279,
+ eyeOfTyr = 387174,
+ righteousProtector = 204074,
+ faithInTheLight = 379043,
+ ferrenMarcussFervor = 378762,
+ faithsArmor = 379017,
+ finalStand = 204077,
+ divineToll = 375576,
+ momentOfGlory = 327193,
+ bulwarkOfTheRighteousFury = 386653,
+ divineResonance = 386738,
+ quickenedInvocations = 379391,
+ },
+ traits = {
+ bulwarkOfLight = 272976,
+ },
+ },
+ -- Retribution
+ [70] = {
+ abilities = {
+ -- divineProtection = 403876,
+ divineStorm = 53385,
+ emancipation = 121783,
+ exorcism = 879,
+ guardianOfAncientKings = 86659,
+ hammerOfTheRighteous = 53595,
+ inquisition = 84963,
+ judgmentsOfTheBold = 111529,
+ sealOfJustice = 20164,
+ templarsVerdict = 85256,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ ancientPower = 86700,
+ -- crusade = 231895,
+ -- divineArbiter = 406975,
+ divineCrusader = 144595,
+ -- divinePurpose = 223819,
+ -- divineRight = 278523,
+ -- empyreanLegacy = 387178,
+ -- empyreanPower = 326733, --286393,
+ inquisition = 84963,
+ -- finalVerdict = 414949,
+ -- selflessHealer = 114250,
+ -- templarStrikes = 406648,
+ },
+ debuffs = {
+ -- executionSentence = 267798,
+ -- expurgation = 383346,
+ -- finalReckoning = 343721,
+ },
+ glyphs = {
+ -- glyphOfWingedVengeance = 57979,
+ },
+ talents = {
+ },
+ traits = {
+ -- divineRight = 278523,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ absolution = 450761,
+ avengingWrath = 31884,
+ blessingOfKings = 20217,
+ blessingOfMight = 19740,
+ blindingLight = 115750,
+ cleanse = 4987,
+ crusaderStrike = 35395,
+ devotionAura = 31821,
+ divineProtection = 498,
+ divineShield = 642,
+ flashOfLight = 19750,
+ hammerOfJustice = 853,
+ hammerOfWrath = 24275,
+ handOfFreedom = 1044,
+ handOfProtection = 1022,
+ handOfSacrifice = 6940,
+ handOfSalvation = 1038,
+ judgment = 20271,
+ layOnHands = 633,
+ rebuke = 96231,
+ reckoning = 62124,
+ redemption = 7328,
+ righteousFury = 25780,
+ sealOfCommand = 105361,
+ sealOfInsight = 20165,
+ sealOfRighteousness = 20154,
+ sealOfTruth = 31801,
+ turnEvil = 10326,
+ wordOfGlory = 85673,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ avengingWrath = 31884,
+ -- blessingOfProtection = 1022,
+ -- consecration = 26573,
+ -- concentrationAura = 317920,
+ -- crusaderAura = 32223,
+ -- devotionAura = 465,
+ divinePurpose = 90174,
+ -- divineResonance = 384029,
+ -- divineShield = 642,
+ -- divineSteed = 221883,
+ -- echoesOfWrath = 423590,
+ holyAvenger = 105809,
+ -- retributionAura = 183435,
+ -- seraphim = 152262,
+ sealOfCommand = 105361,
+ -- vanquishersHammer = 328204,
+ },
+ conduits = {
+ -- expurgation = 339371,
+ -- templarsVindication = 339531,
+ },
+ covenants = {
+ --ashenHallow = 316958,
+ --blessingOfTheSeasons = 328278,
+ --blessingOfAutumn = 388010,
+ --blessingOfSpring = 388013,
+ --blessingOfSummer = 388007,
+ --blessingOfWinter = 388011,
+ --vanquishersHammer = 328204,
+ },
+ debuffs = {
+ -- blindingLight = 105421,
+ forbearance = 25771,
+ -- hammerOfJustice = 853,
+ -- judgment = 197277,
+ -- turnEvil = 10326,
+ -- handOfReckoning = 62124,
+ },
+ glyphs = {
+ -- glyphOfFireFromHeavens = 57954,
+ -- glyphOfPillarOfLight = 146959,
+ -- glyphOfTheLuminousCharger = 89401,
+ -- glyphOfTheQueen = 212642,
+ },
+ talents = {
+ clemency = 105622,
+ divinePurpose = 86172,
+ eternalFlame = 114163,
+ evilIsAPointOfView = 110301,
+ executionSentence = 114157,
+ fistOfJustice = 105593,
+ handOfPurity = 114039,
+ holyAvenger = 105809,
+ holyPrism = 114165,
+ lightsHammer = 114158,
+ longArmOfTheLaw = 87172,
+ pursuitOfJustice = 26023,
+ repentance = 20066,
+ sacredShield = 20925,
+ sanctifiedWrath = 53376,
+ selflessHealer = 85804,
+ speedOfLight = 85499,
+ unbreakableSpirit = 114154,
+ },
+ runeforges = {
+ -- finalVerdict = 337247,
+ -- theMadParagon = 337594,
+ -- theMagistratesJudgment = 337681,
+ -- vanguardsMomentum = 337638,
+ }
+ },
+ },
+ PRIEST = {
+ -- Discipline
+ [256] = {
+ abilities = {
+ angelicFeather = 121536,
+ --clarityOfWill = 152118,
+ divineStar = 110744,
+ desperatePrayer = 19236,
+ evangelism = 246287,
+ flashHeal = 2061,
+ halo = 120517,
+ holyNova = 132157,
+ leapOfFaith = 73325,
+ mindbender = 123040,
+ mindBlast = 8092,
+ mindControl = 205364,
+ mindSear = 48045,
+ mindSoothe = 453,
+ mindVision = 2096,
+ painSuppression = 33206,
+ penance = 47540,
+ --plea = 200829,
+ powerInfusion = 10060,
+ powerWordBarrier = 62618,
+ powerWordRadiance = 194509,
+ powerWordShield = 17,
+ powerWordSolace = 129250,
+ psychicScream = 8122,
+ purgeTheWicked = 204197,
+ purify = 527,
+ rapture = 47536,
+ schism = 214621,
+ shadowfiend = 34433,
+ shadowMend = 186263,
+ shiningForce = 204263,
+ sinsOfTheMany = 198076,
+ spiritShell = 109964,
+ smite = 585,
+ },
+ artifacts = {
+ --sinsOfTheMany = 198074,
+ },
+ buffs = {
+ angelicFeather = 121557,
+ atonement = 194384,
+ bodyAndSoul = 65081,
+ borrowedTime = 197763,
+ depthOfTheShadows = 275544,
+ --clarityOfWill = 152118,
+ innervate = 29166,
+ overloadedWithLight = 223166,
+ penitent = 246519,
+ --powerInfusion = 10060,
+ powerOfTheDarkSide = 198069,
+ powerWordShield = 17,
+ rapture = 47536,
+ speedOfThePious = 197767,
+ spiritShell = 109964,
+ symbolOfHope = 64901,
+ },
+ debuffs = {
+ weakenedSoul = 6788,
+ purgeTheWicked = 204213,
+ schism = 214621,
+ smite = 585,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ angelicFeather = 121536,
+ bodyAndSoul = 64129,
+ castigation = 193134,
+ --clarityOfWill = 152118,
+ contrition = 197419,
+ divineStar = 110744,
+ dominantMind = 205367,
+ evangelism = 246287,
+ --grace = 200309,
+ halo = 120517,
+ lenience = 238063,
+ masochism = 193063,
+ mindbender = 123040,
+ --powerInfusion = 10060,
+ powerWordSolace = 129250,
+ purgeTheWicked = 204197,
+ psychicVoice = 196704,
+ schism = 214621,
+ shadowCovenant = 314867,
+ shieldDiscipline = 197045,
+ spiritShell = 109964,
+ sinsOfTheMany = 280391,
+ shiningForce = 204263,
+ --thePenitent = 200347,
+ twistOfFate = 265259,
+ },
+ traits = {
+ giftOfForgiveness = 277680,
+ },
+ },
+ -- Holy
+ [257] = {
+ abilities = {
+ angelicFeather = 121536,
+ bodyAndMind = 214121,
+ circleOfHealing = 204883,
+ desperatePrayer = 19236,
+ divineHymn = 64843,
+ divineStar = 110744,
+ flashHeal = 2061,
+ guardianSpirit = 47788,
+ holyFire = 14914,
+ holyNova = 132157,
+ holyWordChastise = 88625,
+ holyWordSanctify = 34861,
+ holyWordSerenity = 2050,
+ holyWordSalvation = 265202,
+ heal = 2060,
+ leapOfFaith = 73325,
+ lightOfTuure = 33076,
+ prayerOfHealing = 596,
+ prayerOfMending = 33076,
+ purify = 527,
+ renew = 139,
+ smite = 585,
+ symbolOfHope = 64901,
+ },
+ artifacts = {
+ -- lightOfTuure = 208065,
+ },
+ buffs = {
+ angelicFeather = 121557,
+ blessingOfTuure = 196578,
+ divinity = 197031,
+ echoOfLight = 77489,
+ flashConcentration = 336267,
+ powerOfTheNaaru = 196489,
+ prayerOfMending = 41635,
+ renew = 139,
+ surgeOfLight = 114255,
+ spiritOfRedemption = 27827,
+ },
+ debuffs = {
+
+
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+ flashConcentration = 336266,
+ },
+ talents = {
+ angelicFeather = 121536,
+ apotheosis = 200183,
+ bodyAndSoul = 64129,
+ --bodyAndMind = 214121,
+ divineStar = 110744,
+ --divinity = 197031,
+ halo = 120517,
+ --piety = 197034,
+ shiningForce = 204263,
+ surgeOfLight = 109186,
+ --symbolOfHope = 64901,
+ },
+ },
+ -- Shadow
+ [258] = {
+ abilities = {
+ damnation = 341374,
+ darkAscension = 280711,
+ dispersion = 47585,
+ devouringPlague = 335467,
+ mindBlast = 8092,
+ mindBomb = 205369,
+ mindbender = 200174,
+ mindFlay = 15407,
+ mindSear = 48045,
+ mindVision = 2096,
+ powerInfusion = 10060,
+ powerWordShield = 17,
+ psychicHorror = 64044,
+ psychicScream = 8122,
+ purifyDisease = 213634,
+ shadowMend = 186263,
+ shadowWordVoid = 205351,
+ shadowfiend = 34433,
+ shadowform = 232698,
+ silence = 15487,
+ surrenderToMadness = 193223,
+ vampiricEmbrace = 15286,
+ vampiricTouch = 34914,
+ voidBolt = 205448,
+ devoidBolt = 343355,
+ voidEruption = 228260,
+ voidForm = 228264,
+ },
+ artifacts = {
+ lashOfInsanity = 238137,
+ massHysteria = 194378,
+ sphereOfInsanity = 194179,
+ toThePain = 193644,
+ touchOfDarkness = 194007,
+ unleashTheShadows = 194093,
+ voidCorruption = 194016,
+ voidTorrent = 205065,
+ },
+ buffs = {
+ chorusOfInsanity = 279572,
+ darkThoughts = 341207,
+ dispersion = 47585,
+ dissonantEchoes = 343144,
+ harvestedThoughts = 288343,
+ lingeringInsanity = 199849,
+ powerWordShield = 17,
+ powerInfusion = 10060,
+ shadowyInsight = 124430,
+ shadowform = 232698,
+ surrenderedSoul = 212570,
+ surrenderToMadness = 193223,
+ thoughtsHarvester = 288340,
+ unfurlingDarkness = 341282,
+ void = 211657,
+ voidForm = 194249,
+ voidTorrent = 205065,
+ zeksExterminatus = 236546, -- Legendary Cloak proc
+ },
+ debuffs = {
+ devouringPlague = 335467,
+ hungeringVoid = 345218,
+ mindFlay = 15407,
+ vampiricTouch = 34914,
+ weakenedSoul = 6788,
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+ shadowflamePrism = 336143,
+ },
+ talents = {
+ fortressOfTheMind = 193195,
+ deathAndMadness = 321291,
+ unfurlingDarkness = 341273,
+ bodyAndSoul = 64129,
+ sanlayn = 199855,
+ twistOfFate = 109142,
+ misery = 238558,
+ searingNightmare = 341385,
+ mindBomb = 205369,
+ psychicHorror = 64044,
+ auspiciousSpirits = 155271,
+ psychicLink = 199484,
+ shadowCrash = 205385,
+ damnation = 341374,
+ mindbender = 200174,
+ voidTorrent = 263165,
+ ancientMadness = 341240,
+ hungeringVoid = 345218,
+ surrenderToMadness = 319952,
+ },
+ traits = {
+ chorusOfInsanity = 278661,
+ deathThroes = 278659,
+ searingDialogue = 272788,
+ spitefulApparitions = 277682,
+ thoughtHarvester = 288340,
+ whispersOfTheDamned = 275722,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ ascendedBlast = 325283,
+ ascendedNova = 325020,
+ boonOfTheAscended = 325013,
+ desperatePrayer = 19236,
+ dispelMagic = 528,
+ fade = 586,
+ faeGuardians = 327661,
+ flashHeal = 2061,
+ leapOfFaith = 73325,
+ levitate = 1706,
+ massDispel = 32375,
+ massResurrection = 212036,
+ mindBlast = 8092,
+ mindControl = 605,
+ mindgames = 323673,
+ mindSoothe = 453,
+ mindVision = 2096,
+ powerInfusion = 10060,
+ powerWordFortitude = 21562,
+ powerWordShield = 17,
+ psychicScream = 8122,
+ resurrection = 2006,
+ shadowWordDeath = 32379,
+ shackleUndead = 9484,
+ shadowWordPain = 589,
+ smite = 585,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ classHallSpeed = 224098,
+ boonOfTheAscended = 325013,
+ faeGuardians = 327661,
+ levitate = 111759,
+ powerWordFortitude = 21562,
+ powerWordShield = 17,
+
+ },
+ conduits = {
+ dissonantEchoes = 338342,
+ },
+ covenants = {
+ boonOfTheAscended = 325013,
+ ascendedBlast = 325315,
+ ascendedEruption = 325326,
+ ascendedNova = 325020,
+ faeGuardians = 327661,
+ mindgames = 323673,
+ unholyNova = 324724,
+ },
+ debuffs = {
+ shadowWordPain = 589,
+ weakenedSoul = 6788,
+ wrathfulFaerie = 327703,
+
+ },
+ glyphs = {
+
+ },
+ talents = {
+
+ },
+ },
+ },
+ ROGUE = {
+ -- Assassination
+ [259] = {
+ abilities = {
+ -- ambush = 8676,
+ -- crimsonTempest = 121411,
+ -- deadlyPoison = 315584,
+ -- envenom = 32645,
+ -- eviscerate = 196819,
+ -- exsanguinate = 200806,
+ -- fanOfKnives = 51723,
+ -- garrote = 703,
+ -- mutilate = 1329,
+ -- poisonedKnife = 185565,
+ -- rupture = 1943,
+ -- shadowstep = 36554,
+ -- sinisterStrike = 1752,
+ -- vendetta = 79140,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ blindside = 121153,
+ deadlyPoison = 2823,
+ elaboratePlanning = 193641,
+ envenom = 32645,
+ leechingPoison = 108211,
+ sharpenedBlades = 272916,
+ masterAssassin = 256735,
+ subterfuge = 115192,
+ theDreadlordsDeceit = 208692,
+ },
+ debuffs = {
+ crimsonTempest = 121411,
+ deadlyPoison = 2818,
+ garrote = 703,
+ internalBleeding = 154953,
+ rupture = 1943,
+ surgeOfToxins = 192425,
+ toxicBlade = 245389,
+ vendetta = 79140,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ blindside = 328085,
+ crimsonTempest = 121411,
+ elaboratePlanning = 193640,
+ elusiveness = 79008,
+ exsanguinate = 200806,
+ hiddenBlades = 270061,
+ internalBleeding = 154904,
+ ironWire = 196861,
+ leechingPoison = 280716,
+ masterAssassin = 255989,
+ masterPoisoner = 196864,
+ nightstalker = 14062,
+ poisonBomb = 255544,
+ subterfuge = 108208,
+ venomRush = 152152,
+ },
+ traits = {
+ doubleDose = 273007,
+ echoingBlades = 287649,
+ sharpenedBlades = 272911,
+ shroudedSuffocation = 278666,
+ twistTheKnife = 273488,
+ },
+ runeforges = {
+ dashingScoundrel = 340081,
+ doomblade = 340082,
+ zoldyckInsignia = 340083,
+ duskwalkersPatch = 340084,
+ },
+ },
+ -- Outlaw
+ [260] = {
+ abilities = {
+ adrenalineRush = 13750,
+ ambush = 8676,
+ betweenTheEyes = 315341,
+ bladeFlurry = 13877,
+ bladeRush = 271877,
+ blind = 2094,
+ curseOfTheDreadblades = 202665,
+ dispatch = 2098,
+ ghostlyStrike = 196937,
+ gouge = 1776,
+ grapplingHook = 195457,
+ killingSpree = 51690,
+ masteryMainGauche = 76806,
+ pistolShot = 185763,
+ riposte = 199754,
+ rollTheBones = 315508,
+ sinisterStrike = 193315,
+ dreadblades = 343142,
+ },
+ artifacts = {
+ blackPowder = 216230,
+ bladeDancer = 202507,
+ bladeMaster = 202628,
+ blunderbuss = 202897,
+ blurredTime = 202769,
+ cannonballBarrage = 185767,
+ curseOfTheDreadblades = 202665,
+ cursedEdge = 202463,
+ cursedSteel = 214929,
+ deception = 202755,
+ fatesThirst = 202514,
+ fatebringer = 202524,
+ fortuneStrikes = 202530,
+ fortunesBoon = 202907,
+ fortunesStrike = 202521,
+ ghostlyShell = 202533,
+ greed = 202820,
+ gunslinger = 202522,
+ hiddenBlade = 202573,
+ killingSpree = 51690,
+ loadedDice = 238139,
+ },
+ buffs = {
+ adrenalineRush = 13750,
+ alacrity = 193538,
+ bladeFlurry = 13877,
+ blunderbuss = 202895,
+ broadside = 193356,
+ buriedTreasure = 199600,
+ deadShot = 272940,
+ grandMelee = 193358,
+ greenskinsWaterloggedWristcuffs = 209420,
+ hiddenBlade = 202754,
+ jollyRoger = 199603,
+ loadedDice = 256171,
+ opportunity = 195627,
+ ruthlessPrecision = 193357,
+ rollTheBones = {
+ broadside = 193356,
+ buriedTreasure = 199600,
+ grandMelee = 193358,
+ ruthlessPrecision = 193357,
+ skullAndCrossbones = 199603,
+ trueBearing = 193359,
+ },
+ sharkInfestedWaters = 193357,
+ skullAndCrossbones = 199603,
+ snakeeeyes = 275863, --typo, leaving in not to break stuff
+ snakeeyes = 275863,
+ swordplay = 211669,
+ trueBearing = 193359,
+ wits = 288988,
+ },
+ debuffs = {
+ ghostlyStrike = 196937,
+ gouge = 1776,
+ betweenTheEyes = 199804,
+ preyOnTheWeak = 255909,
+ blind = 2094,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ acrobaticStikes = 196924, -- typo, leaving in to not break old profiles
+ acrobaticStrikes = 196924,
+ dirtyTricks = 108216,
+ bladeRush = 271877,
+ ghostlyStrike = 196937,
+ grapplingHook = 256188,
+ hitAndRun = 196922,
+ ironStomach = 193546,
+ killingSpree = 51690,
+ weaponmaster = 200733,
+ quickDraw = 196938,
+ blindingPowder = 256165,
+ loadedDice = 256170,
+ dancingSteel = 272026,
+ dreadblades = 343142,
+ },
+ traits = {
+ deadshot = 272935,
+ aceupyoursleeve = 278676,
+ snakeeyes = 275846,
+ keepYourWitsAboutYou = 288979,
+
+ },
+ runeforges = {
+ greenskinsWickers = 340085,
+ guileCharm = 340086,
+ celerity = 340087,
+ concealedBlunderbuss = 340088,
+ },
+ conduits = {
+ countTheOdds = 341546,
+ },
+ },
+ -- Subtlety (MoP Classic - 5.4.8)
+ [261] = {
+ abilities = {
+ ambush = 8676,
+ backstab = 53,
+ crimsonTempest = 121411,
+ eviscerate = 2098,
+ fanOfKnives = 51723,
+ hemorrhage = 16511,
+ preparation = 14185,
+ premeditation = 14183,
+ rupture = 1943,
+ shadowBlades = 121471,
+ shadowDance = 121471, -- MoP Classic uses same ID as Shadow Blades
+ shadowstep = 36554,
+ shurikenToss = 114014,
+ sliceAndDice = 5171,
+ vanish = 1856,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ anticipation = 115189, -- Talent buff for extra combo points
+ masterOfSubtlety = 31665, -- Shadow Dance / Stealth buff
+ shadowBlades = 121471,
+ shadowDance = 121471, -- MoP Classic uses same ID as Shadow Blades
+ shadowmeld = 20580, -- Night Elf racial
+ sliceAndDice = 5171,
+ sleightOfHand = 145211, -- Proc from Ambush
+ stealth = 1784,
+ subterfuge = 115192, -- Talent - Stealth linger
+ vanish = 1856,
+ },
+ debuffs = {
+ crimsonTempest = 122233,
+ findWeakness = 91023, -- Shadow Dance debuff
+ hemorrhage = 16511,
+ rupture = 1943,
+ },
+ glyphs = {
+ glyphOfAmbush = 56813,
+ glyphOfBackstab = 63254,
+ glyphOfHemorrhage = 56807,
+ glyphOfPreparation = 63249,
+ glyphOfShadowDance = 63253,
+ glyphOfSliceAndDice = 146638,
+ glyphOfVanish = 56814,
+ },
+ talents = {
+ -- Tier 1 (Level 15)
+ nightstalker = 14062,
+ subterfuge = 108208,
+ shadowFocus = 108209,
+ -- Tier 2 (Level 30) - Shared
+ deadlyThrow = 26679,
+ combatReadiness = 74001,
+ -- Tier 3 (Level 45)
+ cheatDeath = 31230,
+ leechingPoison = 108211,
+ elusiveness = 79008,
+ -- Tier 4 (Level 60)
+ cloakAndDagger = 138106,
+ shadowstep = 36554,
+ burstOfSpeed = 108212,
+ -- Tier 5 (Level 75)
+ preyOnTheWeak = 131511,
+ paralyticPoison = 108215,
+ dirtyTricks = 108216,
+ -- Tier 6 (Level 90)
+ shurikenToss = 114014,
+ markedForDeath = 137619,
+ anticipation = 114015,
+ },
+ traits = {
+
+ },
+ runeforges = {
+
+ },
+ conduits = {
+
+ },
+ },
+ -- All (MoP Classic - 5.4.8)
+ Shared = {
+ abilities = {
+ ambush = 8676,
+ blind = 2094,
+ cheapShot = 1833,
+ cloakOfShadows = 31224,
+ combatReadiness = 74001, -- MoP defensive CD
+ cripplingPoison = 3408,
+ deadlyPoison = 2823,
+ distract = 1725,
+ evasion = 5277,
+ eviscerate = 2098,
+ feint = 1966,
+ gouge = 1776,
+ instantPoison = 6603, -- MoP version
+ kick = 1766,
+ kidneyShot = 408,
+ mindNumbingPoison = 5761, -- MoP version (previously Crippling in later expansions)
+ paralyticPoison = 108211, -- MoP poison
+ pickLock = 1804,
+ pickPocket = 921,
+ redirect = 73981, -- MoP ability
+ recuperate = 73651, -- MoP healing finisher
+ sap = 6770,
+ shroudOfConcealment = 114018, -- MoP group stealth
+ sinisterStrike = 1752,
+ sliceAndDice = 5171,
+ sprint = 2983,
+ stealth = 1784,
+ throw = 2764, -- MoP version
+ tricksOfTheTrade = 57934,
+ vanish = 1856,
+ woundPoison = 8679,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ cloakOfShadows = 31224,
+ combatReadiness = 74001,
+ cripplingPoison = 3408,
+ deadlyPoison = 2823,
+ evasion = 5277,
+ feint = 1966,
+ instantPoison = 6603,
+ mindNumbingPoison = 5761,
+ paralyticPoison = 108211,
+ recuperate = 73651,
+ shroudOfConcealment = 114018,
+ sliceAndDice = 5171,
+ sprint = 2983,
+ stealth = 1784,
+ tricksOfTheTrade = 57934,
+ vanish = 1856,
+ woundPoison = 8679,
+ },
+ conduits = {
+
+ },
+ covenants = {
+
+ },
+ debuffs = {
+ blind = 2094,
+ cheapShot = 1833,
+ cripplingPoison = 3408,
+ garrote = 703,
+ gouge = 1776,
+ kidneyShot = 408,
+ mindNumbingPoison = 5761,
+ paralyticPoison = 113952, -- Debuff version
+ rupture = 1943,
+ sap = 6770,
+ woundPoison = 8679,
+ },
+ glyphs = {
+ glyphOfAdrenalineRush = 56803,
+ glyphOfBlind = 91299,
+ glyphOfCloakOfShadows = 63269,
+ glyphOfExposedArmor = 56805,
+ glyphOfFanOfKnives = 63254,
+ glyphOfFeint = 63257,
+ glyphOfGarrote = 56812,
+ glyphOfGouge = 56809,
+ glyphOfKick = 63252,
+ glyphOfKillingSpree = 63249,
+ glyphOfSap = 56798,
+ glyphOfSprint = 63250,
+ glyphOfTricksOfTheTrade = 63256,
+ },
+ talents = {
+ -- Tier 2 (Level 30) - Shared across all Rogue specs
+ deadlyThrow = 26679,
+ combatReadiness = 74001,
+ },
+ runeforges = {
+
+ },
+ },
+ },
+ SHAMAN = {
+ -- Elemental
+ [262] = {
+ abilities = {
+ earthquake = 61882,
+ earthShock = 8042,
+ eyeOfTheStorm = 157375,
+ fireBlast = 57984,
+ fireElemental = 198067,
+ hardenSkin = 118337,
+ immolate = 118297,
+ lavaBeam = 114074,
+ meteor = 117588,
+ pulverize = 118345,
+ windGust = 157331,
+ },
+ artifacts = {
+ },
+ conduits = {
+ callOfFlame = 338303,
+ },
+ buffs = {
+ ascendance = 114050,
+ bloodlust = 2825,
+ echoingShock = 320125,
+ elementalFocus = 16164,
+ elementalMastery = 16166,
+ emberTotem = 210658,
+ hardenSkin = 118337,
+ heroism = 32182,
+ iceFury = 210714,
+ lavaSurge = 77762,
+ masterOfTheElements = 260734,
+ powerOfTheMaelstrom = 191877,
+ resonanceTotem = 202192,
+ stormKeeper = 191634,
+ stormTotem = 210652,
+ surgeOfPower = 285514,
+ tailwindTotem = 210659,
+ tectonicThunder = 286949,
+ windGust = 263806,
+ },
+ debuffs = {
+ flameShock = 188389,
+ immolate = 118297,
+ lightningRod = 197209,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ aftershock = 273221,
+ ancestralGuidance = 108281,
+ ascendance = 114050,
+ earthenRage = 170374,
+ echoingShock = 320125,
+ echoOfTheElements = 333919,
+ elementalBlast = 117014,
+ iceFury = 210714,
+ liquidMagmaTotem = 192222,
+ masterOfTheElements = 16166,
+ primalElementalist = 117013,
+ staticDischarge = 342243,
+ stormElemental = 192249,
+ stormKeeper = 191634,
+ surgeOfPower = 262303,
+ unlimitedPower = 260895,
+
+ },
+ traits = {
+ naturalHarmony = 278697,
+ tectonicThunder = 286949,
+ },
+ },
+ -- Enhancement
+ [263] = {
+ abilities = {
+ feralSpirit = 51533,
+ fireNova = 1535,
+ lavaLash = 60103,
+ searingTotem = 3599,
+ shamanisticRage = 30823,
+ spiritWalk = 58875,
+ stormblast = 115356,
+ stormstrike = 17364,
+ windfuryWeapon = 8232,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ ascendance = 114051,
+ -- ashenCatalyst = 390371,
+ -- awakeningStorms = 462131,
+ -- cracklingSurge = 224127,
+ -- crashLightning = 187878,
+ -- clCrashLightning = 333964,
+ -- convergingStorms = 198300,
+ -- doomWinds = 384352,
+ -- earthenWeapon = 392375,
+ -- feralSpirit = 333957,
+ -- hailstorm = 334196,
+ -- hotHand = 215785,
+ -- icyEdge = 224126,
+ -- legacyOfTheFrostWitch = 384451,
+ -- maelstromOfElements = 394677,
+ maelstromWeapon = 53817,
+ -- moltenWeapon = 224125,
+ -- primordialWave = 327164,
+ -- splinteredElements = 354648,
+ -- tempest = 454015,
+ -- stormkeeper = 320137,
+ -- stormbringer = 201845,
+ },
+ debuffs = {
+ -- doomWinds = 335904,
+ -- lashingFlames = 334168,
+ },
+ glyphs = {
+ glyphOfFrostShock = 55443,
+ },
+ talents = {
+ },
+ traits = {
+ -- lightningConduit = 275389,
+ -- naturalHarmony = 278697,
+ -- primalPrimer = 272992,
+ -- strengthOfTheEarth = 273461,
+ },
+ },
+ -- Restoration
+ [264] = {
+ abilities = {
+ ancestralVision = 212048,
+ waterShield = 52127,
+ healingRain = 73920,
+ healingTideTotem = 108280,
+ healingWave = 77472,
+ manaTideTotem = 16191,
+ purifySpirit = 77130,
+ riptide = 61295,
+ spiritLinkTotem = 98008,
+ recallCloudburstTotem = 201764,
+ },
+ artifacts = {
+ -- giftOfTheQueen = 207778,
+ },
+ buffs = {
+ ascendance = 114052,
+ cloudburstTotem = 157504,
+ waterShield = 52127,
+ healingRain = 73920,
+ heavyRainfall = 338344,
+ jonatsFocus = 210607,
+ lavaSurge = 77762,
+ riptide = 61295,
+ tidalWaves = 53390,
+ unleashLife = 73685,
+ undulation = 216251,
+ swirlingCurrents = 338340,
+ },
+ conduits = {
+ heavyRainfall = 338343,
+ },
+ debuffs = {
+ flameShock = 188389,
+ },
+ glyphs = {
+ },
+ talents = {
+ ancestralVigor = 207401,
+ ancestralProtectionTotem = 207399,
+ ascendance = 114052,
+ cloudburstTotem = 157153,
+ deluge = 200076,
+ downpour = 207778,
+ earthenWallTotem = 198838,
+ echoOfTheElements = 108283,
+ flashFlood = 280614,
+ highTide = 157154,
+ sugeofEarth = 320746,
+ torrent = 200072,
+ undulation = 200071,
+ unleashLife = 73685,
+ wellspring = 197995,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ ancestralSpirit = {2008,20609,20610,20776,20777},
+ astralRecall = 556,
+ bloodlust = 2825,
+ chainHeal = {1064,10622,10623,25422,25423},
+ chainLightning = {421,930,2860,10605,25439,25442},
+ cureDisease = 2870,
+ curePoison = 526,
+ diseaseCleansingTotem = 8170,
+ earthElementalTotem = 2062,
+ earthShock = {8042,8044,8045,8046,10412,10413,10414,25454},
+ earthbindTotem = 2484,
+ farSight = 6196,
+ fireElementalTotem = 2894,
+ fireNovaTotem = {1535,8498,8499,11314,11315,25546,25547},
+ fireResistanceTotem = {8184,10537,10538,25563},
+ flameShock = {8050,8052,8053,10447,10448,29228,25457},
+ flametongueTotem = {8227,8249,10526,16387,25557},
+ flametongueWeapon = {8024,8027,8030,16339,16341,16342,25489},
+ frostResistanceTotem = {8181,10478,10479,25560},
+ frostShock = {8056,8058,10472,10473,25464},
+ frostbrandWeapon = {8033,8038,10456,16355,16356,25500},
+ ghostWolf = 2645,
+ graceOfAirTotem = {8835,10627,25359},
+ groundingTotem = 8177,
+ healingStreamTotem = {5394,6375,6377,10462,10463,25567},
+ healingWave = {331,332,547,913,939,959,8005,10395,10396,25357,25391,25396},
+ heroism = 32182,
+ lesserHealingWave = {8004,8008,8010,10466,10467,10468,25420},
+ lightningBolt = {403,529,548,915,943,6041,10391,10392,15207,15208,25448,25449},
+ lightningShield = {324,325,905,945,8134,10431,10432,25469,25472},
+ magmaTotem = {8190,10585,10586,10587,25552},
+ manaSpringTotem = {5675,10495,10496,10497,25570},
+ natureResistanceTotem = {10595,10600,10601,25574},
+ poisonCleansingTotem = 8166,
+ purge = {370,8012},
+ reincarnation = 20608,
+ rockbiterWeapon = {8017,8018,8019,10399,16314,16315,16316,25479,25485},
+ searingTotem = {3599,6363,6364,6365,10437,10438,25533},
+ sentryTotem = 6495,
+ stoneclawTotem = {5730,6390,6391,6392,10427,10428,25525},
+ stoneskinTotem = {8071,8154,8155,10406,10407,10408,25508,25509},
+ strengthOfEarthTotem = {8075,8160,8161,10442,25361,25528},
+ totemicRecall = 36936,
+ tranquilAirTotem = 25908,
+ tremorTotem = 8143,
+ waterBreathing = 131,
+ waterShield = {24398,33736},
+ waterWalking = 546,
+ windfuryTotem = {8512,10613,10614,25585,25587},
+ windfuryWeapon = {8232,8235,10486,16362,25505},
+ windwallTotem = {15107,15111,15112,25577},
+ wrathOfAirTotem = 3738,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ -- ancestralSwiftness = 16188,
+ -- astralShift = 108271,
+ -- earthShield = 974,
+ -- flametongueWeapon = 8024,
+ -- flameShock = 8050,
+ -- ghostWolf = 2645,
+ lightningShield = {324,325,905,945,8134,10431,10432,25469,25472},
+ -- skyfury = 462854,
+ -- spiritwalkersGrace = 79206,
+ -- unleashFlame = 73683,
+ waterShield = {24398,33736},
+ -- waterWalking = 546,
+ },
+ conduits = {
+ -- astralProtection = 337964,
+ },
+ covenants = {
+ -- chainHarvest = 320674,
+ -- faeTransfusion = 328923,
+ -- primordialWave = 326059,
+ -- vesperTotem = 324386,
+ },
+ debuffs = {
+ flameShock = {8050,8052,8053,10447,10448,29228,25457},
+ -- frostShock = 196840,
+ -- hex = 51514,
+ },
+ glyphs = {
+
+ },
+ runeforges = {
+ -- chainsOfDevastation = 336735,
+ -- doomWinds = 335902,
+ -- elementalEquilibrium = 336730,
+ -- primalLavaActuators = 335895,
+ -- echoesOfGreatSundering = 336215,
+ -- seedsOfRampantGrowth = 356218,
+ -- skybreakersFieryDemise = 336734,
+ -- spiritwalkersTidalTotem = 335891,
+ -- deeptremorStone = 336739,
+ -- windspeakersLavaResurgence = 336063,
+ -- deeplyRootedElements = 336738,
+ },
+ talents = {
+ -- ancestralGuidance = 108281,
+ -- ancestralSwiftness = 16188,
+ -- astralShift = 108271,
+ -- callOfTheElements = 108285,
+ -- conductivity = 108282,
+ -- earthgrabTotem = 51485,
+ -- echoOfTheElements = 108283,
+ -- elementalBlast = 117014,
+ -- elementalMastery = 16166,
+ -- frozenPower = 63374,
+ -- naturesGuardian = 30884,
+ -- primalElementalist = 117013,
+ -- rushingStreams = 147074,
+ -- stoneBulwarkTotem = 108270,
+ -- totemicPersistence = 108284,
+ -- totemicProjection = 108287,
+ -- unleashedFury = 117012,
+ -- windwalkTotem = 108273,
+ },
+ talentsHeroic = {
+ -- -- Enhance / Elemental Stormbringer
+ -- unlimitedPower = 454391,
+ -- supercharge = 455110,
+ -- voltaicSurge = 454919,
+ -- awakeningStorms = 455129,
+ -- stormcaller = 454021,
+ -- stormSwell = 455088,
+ -- arcDischarge = 455096,
+ -- conductiveEnergy = 455123,
+ -- shockingGrasp = 454022,
+ -- rollingThunder = 454026,
+ -- naturesProtection = 454027,
+ -- surgingCurrents = 454372,
+ -- -- Enhance / Restoration Totemic
+ -- surgingTotem = 444995,
+ -- totemicRebound = 445025,
+ -- oversizedTotems = 445026,
+ -- swiftRecall = 445027,
+ -- imbuementMastery = 445028,
+ -- whirlingElements = 445024,
+ -- amplificationCore = 445029,
+ -- oversurge = 445030,
+ -- windBarrier = 445031,
+ -- pulseCapacitor = 445032,
+ -- supportiveImbuements = 445033,
+ -- livelyTotems = 445034,
+ -- reactivity = 445035,
+ -- totemicCoordination = 445036,
+ -- earthsurge = 455590,
+ -- -- Elemental / Restoration Farseer
+ -- callOfTheAncestors = 443450,
+ -- latentWisdom = 443449,
+ -- ancientFellowship = 443423,
+ -- offeringFromBeyond = 443451,
+ -- naturalHarmony = 443442,
+ -- earthenCommunion = 443441,
+ -- ancestralSwiftness = 443454,
+ -- heedMyCall = 443444,
+ -- routineCommunication = 443445,
+ -- primordialCapacity = 443448,
+ -- maelstromSupremacy = 443447,
+ -- elementalReverb = 443418,
+ -- spiritwalkersMomentum = 443425,
+ -- finalCalling = 443446,
+ },
+ totem = {
+ air = {
+ -- stormlashTotem = 120668,
+ -- windwalkTotem = 108273,
+ -- capacitorTotem = 108269,
+ -- groundingTotem = 8177,
+ },
+ earth = {
+ earthbindTotem = 2484,
+ earthElementalTotem = 2062,
+ -- earthquake = 61882,
+ stoneskinTotem = 8072,
+ stoneclawTotem = 5730,
+ strengthOfEarthTotem = 8075,
+ tremorTotem = 8143,
+ },
+ fire = {
+ -- fireElementalTotem = 2894,
+ fireNovaTotem = 1535,
+ -- flameShock = 8050,
+ -- magmaTotem = 8190,
+ searingTotem = 3599,
+ },
+ water = {
+ -- healingStreamTotem = 5394,
+ -- healingTideTotem = 108280,
+ -- manaTideTotem = 16191,
+ },
+ }
+ },
+ },
+ WARLOCK = {
+ -- Affliction
+ [265] = {
+ abilities = {
+ agony = 980,
+ commanddemon = 119898,
+ corruption = 172,
+ darkSoul = 113860,
+ deathbolt = 264106,
+ demonicGateway = 311699,
+ drainLife = 234153,
+ drainSoul = 198590,
+ felDomination = 333889,
+ grimoireOfSacrifice = 108503,
+ haunt = 48181,
+ maleficRapture = 324536,
+ mortalCoil = 6789,
+ phantomSingularity = 205179,
+ reapSouls = 216698,
+ seedOfCorruption = 27243,
+ shadowBolt = 232670,
+ shadowBolt2 = 686,
+ shadowLock = 171138,
+ siphonLife = 63106,
+ spellLock = 19647,
+ spellLockgrimoire = 132409,
+ soulEffigy = 205178,
+ soulRot = 325640,
+ soulSwap = 386951,
+ soulTap = 387073,
+ summonDarkglare = 205180,
+ unstableAffliction = 30108,
+ vileTaint = 278350,
+ },
+ artifacts = {
+ },
+ buffs = {
+ cascadingCalamity = 275378,
+ compoundingHorror = 199281,
+ darkSoul = 113860,
+ deadwindHarvester = 216708,
+ demonicPower = 196099,
+ empoweredLifeTap = 235156,
+ felDomination = 333889,
+ nightfall = 264571,
+ tormentedSouls = 216695,
+ wrathOfConsumption = 199646,
+ inevitableDemise = 273525,
+ },
+ debuffs = {
+ agony = 980,
+ corruption = 146739,
+ haunt = 48181,
+ phantomSingularity = 205179,
+ seedOfCorruption = 27243,
+ siphonLife = 63106,
+ shadowEmbrace = 32390,
+ unstableAffliction = 316099,
+ unstableAffliction2 = 233496,
+ unstableAffliction3 = 233497,
+ unstableAffliction4 = 233498,
+ unstableAffliction5 = 233499,
+ vileTaint = 278350,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ absoluteCorruption = 196103,
+ creepingDeath = 264000,
+ darkCaller = 334183,
+ darkfury = 264874,
+ seedOfCorruption = 196226,
+ darkSoul = 113860,
+ demonicSacrifice = 108503,
+ drainSoul = 198590,
+ grimoireOfSacrifice = 108503,
+ haunt = 48181,
+ howlOfTerror = 5484,
+ inevitableDemise = 334319,
+ nightfall = 108558,
+ phantomSingularity = 205179,
+ siphonLife = 63106,
+ soulConduit = 215941,
+ sowTheSeeds = 196226,
+ vileTaint = 278350,
+ writheInAgony = 196102,
+ },
+ traits = {
+ cascadingCalamity = 275372,
+ dreadfulCalling = 278727,
+ inevitableDemise = 273521,
+ pandemicInvocation = 289364,
+ }
+ },
+ -- Demonology
+ [266] = {
+ abilities = {
+ axeToss = 89766,
+ bilescourgeBombers = 267211,
+ callDreadstalkers = 104316,
+ commandDemon = 119898,
+ corruption = 172,
+ demonbolt = 264178,
+ demonicEmpowerment = 193396,
+ demonicStrength = 267171,
+ demonwrath = 193440,
+ doom = 603,
+ drainLife = 234153,
+ felFirebolt = 104318,
+ felstorm = 89751,
+ grimoireFelguard = 111898,
+ guillotine = 386833,
+ handOfGuldan = 105174,
+ implosion = 196277,
+ netherPortal = 267217,
+ powerSiphon = 264130,
+ shadowBolt = 686,
+ shadowflame = 205181,
+ shadowLock = 171138,
+ shadowsBite = 387322,
+ spellLock = 19647,
+ summonDarkglare = 205180,
+ summonDemonicTyrant = 265187,
+ summonVilefiend = 264119,
+ thalkielsConsumption = 211714,
+ },
+ artifacts = {
+ thalkielsAscendance = 238145,
+ thalkielsConsumption = 211714,
+ },
+ buffs = {
+ demonicCalling = 205146,
+ demonicCore = 267102,
+ demonicEmpowerment = 193396,
+ demonicPower = 265273,
+ demonwrath = 193440,
+ explosivePotential = 275398,
+ forbiddenKnowledge = 278738,
+ netherPortal = 267218,
+ shadowsBite = 272945,
+ shadowyInspiration = 196269,
+ },
+ debuffs = {
+ doom = 603,
+ shadowflame = 205181,
+ corruption = 172,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ bilescourgeBombers = 267211,
+ darkfury = 264874,
+ demonicStrength = 267171,
+ demonicCalling = 205145,
+ doom = 603,
+ dreadlash = 264078,
+ fromTheShadows = 267170,
+ grimoireFelguard = 111898,
+ howlOfTerror = 5484,
+ innerDemons = 267216,
+ netherPortal = 267217,
+ powerSiphon = 264130,
+ sacrificedSouls = 267214,
+ summonVilefiend = 264119,
+ soulConduit = 215941,
+
+ },
+ traits = {
+ balefulInvocation = 287059,
+ explosivePotential = 275395,
+ shadowsBite = 272944,
+ }
+ },
+ -- Destruction
+ [267] = {
+ abilities = {
+ cataclysm = 152108,
+ channelDemonfire = 196447,
+ chaosBolt = 116858,
+ conflagrate = 17962,
+ corruption = 172,
+ commandDemon = 119898,
+ darkSoul = 113858,
+ devourMagic = 19505,
+ dimensionalRift = 196586,
+ drainLife = 234153,
+ felDomination = 333889,
+ grimoireOfSacrifice = 108503,
+ havoc = 80240,
+ immolate = 348,
+ incinerate = 29722,
+ rainOfFire = 5740,
+ shadowBolt = 686,
+ shadowburn = 17877,
+ shadowLock = 171138,
+ singeMagic = 119905,
+ singeMagicGrimoire = 132411,
+ soulFire = 6353,
+ spellLock = 19647,
+ spellLockgrimoire = 132409,
+ summonInfernal = 1122,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ backdraft = 117828, --196406,
+ crashingChaos = 277706,
+ darkSoul = 113858,
+ darkSoulInstability = 113858,
+ demonicPower = 196099,
+ empoweredLifeTap = 235156,
+ felDomination = 333889,
+ lessonsOfSpaceTime = 236174,
+ lordOfFlames = 224103,
+ },
+ debuffs = {
+ conflagrate = 265931,
+ eradication = 196414,
+ immolate = 157736,
+ havoc = 80240,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ cataclysm = 152108,
+ channelDemonfire = 196447,
+ darkFury = 264874,
+ darkSoul = 113858,
+ darkSoulInstability = 113858,
+ eradication = 196412,
+ flashover = 267115,
+ fireAndBrimstone = 196408,
+ grimoireOfSacrifice = 108503,
+ grimoireOfSupremacy = 266086,
+ howlOfTerror = 5484,
+ inferno = 270545,
+ internalCombustion = 266134,
+ reverseEntropy = 205148,
+ roaringBlaze = 205184,
+ shadowburn = 17877,
+ soulFire = 6353,
+ soulConduit = 215941,
+ },
+ traits = {
+ crashingChaos = 277644
+ }
+ },
+ -- Inital Warlock (1-10)
+ [1454] = {
+ abilities = {
+ controlDemon = 93375,
+ corruption = 172,
+ curseOfWeakness = 702,
+ drainLife = 234153,
+ shadowBolt = 686,
+ },
+ buffs = {
+
+ },
+ debuffs = {
+ corruption = 146739,
+ curseOfWeakness = 702,
+ drainLife = 234153,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ amplifyCurse = 328774,
+ banish = 710,
+ burningRush = 111400,
+ curseOfExhaustion = 334275,
+ curseOfTongues = 1714,
+ curseOfWeakness = 702,
+ createHealthstone = 6201,
+ createSoulwell = 29893,
+ commandDemon = 119898,
+ corruption = 172,
+ darkPact = 108416,
+ demonicGateway = 111771,
+ demonicCircle = 48018,
+ demonicTeleport = 48020,
+ devourMagic = 19505,
+ eyeOfKilrogg = 126,
+ felDomination = 333889,
+ fear = 5782,
+ grimoireFelhunter = 111897,
+ grimoireImp = 111859,
+ grimoireOfService = 108501,
+ grimoireSuccubus = 111896,
+ grimoireVoidwalker = 111895,
+ healthFunnel = 755,
+ inquisitorsGaze = 386344,
+ howlOfTerror = 5484,
+ lifeTap = 1454,
+ mortalCoil = 6789,
+ ritualOfDoom = 342601,
+ ritualOfSummoning = 698,
+ shadowflame = 384069,
+ shadowfury = 30283,
+ shadowBulwark = 119907,
+ spellLock = 19647,
+ seduction = 6358,
+ singeMagic = 89808,
+ soulburn = 385899,
+ soulHarvest = 196098,
+ soulstone = 20707,
+ summonSoulkeeper = 386256,
+ --summonDoomguard = 18540,
+ subjugateDemon = 1098,
+ summonFelguard = 30146,
+ summonFelhunter = 691,
+ summonFelImp = 688,
+ summonImp = 688,
+ summonInfernal = 1122,
+ summonSuccubus = 366222,
+ summonVoidwalker = 697,
+ summonWrathguard = 112870,
+ unendingBreath = 5697,
+ unendingResolve = 104773,
+ unstableaffliction = 316099,
+
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ burningRush = 111400,
+ demonicCircle = 48018,
+ demonicSynergy = 171982,
+ felDomination = 333889,
+ healthFunnel = 755,
+ grimoireOfSacrifice = 196099,
+ sindoreiSpite = 208871,
+ soulHarvest = 196098,
+ soulstone = 20707,
+ unendingBreath = 5697,
+ unendingResolve = 104773,
+ },
+ conduits = {
+ corruptingLeer = 339455,
+
+ },
+ covenants = {
+ decimatingBolt = 325289,
+ impendingCatastrophe = 321792,
+ scouringTithe = 312321,
+ },
+ debuffs = {
+ decimatingBolt = 325289,
+ impendingCatastrophe = 321792,
+ scouringTithe = 312321,
+ soulRot = 325640,
+ fear = 5782,
+ curseOfExhaustion = 334275,
+ curseOfTongues = 1714,
+ curseOfWeakness = 702,
+ },
+ glyphs = {
+ glyphOfTheFelImp = 219424,
+ },
+ talents = {
+ burningRush = 111400,
+ darkPact = 108416,
+ demonSkin = 219272,
+ mortalCoil = 6789,
+
+ },
+ },
+ },
+ WARRIOR = {
+ -- Arms
+ [71] = {
+ abilities = {
+ charge = 1250619,
+ colossusSmash = 86346,
+ dieByTheSword = 118038,
+ mortalStrike = 12294,
+ overpower = 7384,
+ slam = 1464,
+ sweepingStrikes = 12328,
+ whirlwind = 1680,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ -- ashenJuggernaut = 335234,
+ -- avatar = 389748,
+ -- battlelord = 346369,
+ -- battleStance = 386164,
+ -- bladestorm = 446035,
+ -- collateralDamage = 334783,
+ colossusSmash = 86346,
+ -- crushingAssault = 278826,
+ -- deadlyCalm = 262228,
+ -- defensiveStance = 386208,
+ -- exploiter = 335452,
+ -- imminentDemise = 445606,
+ -- inForTheKill = 215550,
+ -- juggernaut = 383290,
+ -- mercilessBonegrinder = 383316,
+ -- opportunist = 456120,
+ -- overpower = 400805,
+ -- recklessness = 389722,
+ -- stoneHeart = 225947,
+ -- suddenDeath = 52437,
+ suddenExecute = 139958,
+ sweepingStrikes = 12328,
+ tasteForBlood = 60503,
+ -- testOfMight = 275540,
+ },
+ conduits = {
+ -- ashenJuggernaut = 335232,
+ },
+ covenants = {
+ -- condemn = 317349,
+ -- condemnMassacre = 330334,
+ },
+ debuffs = {
+ colossusSmash = 108126,
+ -- deepWounds = 262115,
+ -- executionersPrecision = 272870,
+ -- markedForExecution = 445584,
+ -- hamstring = 1715,
+ -- ravager = 227876,
+ -- rend = 772,
+ },
+ glyphs = {
+ -- glyphOfThunderStrike = 68164,
+ },
+ talents = {
+ -- armoredToTheTeeth = 384124,
+ -- arterialBleed = 440995,
+ -- avatar = 107574,
+ -- barbaricTraining = 383082,
+ -- battleStance = 386164,
+ -- battlelord = 386630,
+ -- blademastersTorment = 390138,
+ -- bladestorm = 227847,
+ -- bloodborne = 383287,
+ -- bloodletting = 383154,
+ -- bloodsurge = 384361,
+ -- bluntInstruments = 383442,
+ -- boneshaker = 429639,
+ -- brutalFinish = 446085,
+ -- cleave = 845,
+ -- collateralDamage = 334779,
+ -- colossalMight = 429634,
+ -- colossusSmash = 167105,
+ -- criticalThinking = 389306,
+ -- crushingForce = 382764,
+ -- cullingCyclone = 444778,
+ -- danceOfDeath = 390713,
+ -- -- deadlyCalm = 262228,
+ -- deathDrive = 444770,
+ -- deftExperience = 389308,
+ -- demolish = 436358,
+ -- dieByTheSword = 118038,
+ -- dominanceOfTheColossus = 429636,
+ -- dreadnaught = 262150,
+ -- earthquaker = 440992,
+ -- enduranceTraining = 382940,
+ -- executionersPrecision = 386634,
+ -- exhilaratingBlows = 383219,
+ -- fatality = 383703,
+ -- fervorOfBattle = 202316,
+ -- fierceFollowthrough = 444773,
+ -- finishingBlows = 400205,
+ -- frothingBerserker = 392792,
+ -- fueledByViolence = 383103,
+ -- ignorePain = 190456,
+ -- imminentDemise = 444769,
+ -- impale = 383430,
+ -- improvedExecute = 316405,
+ -- improvedOverpower = 385571,
+ -- improvedSweepingStrikes = 383155,
+ -- inForTheKill = 248621,
+ -- juggernaut = 383292,
+ -- martialExpert = 429638,
+ -- martialProwess = 316440,
+ -- massacre = 281001,
+ -- mercilessBonegrinder = 383317,
+ -- mortalStrike = 12294,
+ -- mountainOfMuscleAndScars = 429642,
+ -- noStrangerToPain = 429644,
+ -- oneAgainstMany = 429637,
+ -- opportunist = 444774,
+ -- overpower = 7384,
+ -- overwhelmingBlades = 444772,
+ -- practicedStrikes = 429647,
+ -- preciseMight = 431548,
+ -- reapTheStorm = 444775,
+ -- relentlessPursuit = 444776,
+ -- rend = 772,
+ -- secondWind = 29838,
+ -- sharpenedBlades = 383341,
+ -- showNoMercy = 444771,
+ -- skullsplitter = 260643,
+ -- slayersDominance = 444767,
+ -- slayersMalice = 444779,
+ -- spitefulSerenity = 400314,
+ -- stormOfSwords = 385512,
+ -- stormWall = 388807,
+ -- strengthOfArms = 400803,
+ -- suddenDeath = 29725,
+ -- tactician = 184783,
+ -- testOfMight = 385008,
+ -- tideOfBattle = 429641,
+ -- twohandedWeaponSpecialization = 382896,
+ -- unhinged = 386628,
+ -- unrelentingOnslaught = 444780,
+ -- valorInVictory = 383338,
+ -- veteranVitality = 440993,
+ -- viciousAgility = 444777,
+ -- warMachine = 262231,
+ -- warbreaker = 262161,
+ -- warlordsTorment = 390140,
+ },
+ traits = {
+ -- seismicWave = 277639,
+ -- testOfMight = 275529,
+ }
+ },
+ -- Fury
+ [72] = {
+ abilities = {
+ crushingBlow = 335097,
+ enrage = 184361,
+ execute = 5308,
+ whirlwind = 190411,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ ashenJuggernaut = 392537,
+ avatar = 107574,
+ berserkerStance = 386196,
+ bladestorm = 446035,
+ bloodbath = 461288,
+ brutalFinish = 446918,
+ burstOfPower = 437121,
+ crushingBlow = 396752,
+ -- deathWish = 199261,
+ enrage = 184362,
+ enragedRegeneration = 184364,
+ -- frenzy = 335082,
+ meatCleaver = 85739,
+ -- mercilessBonegrinder = 346574,
+ opportunist = 456120,
+ recklessness = 1719,
+ suddenDeath = 280776,
+ -- whirlwind = 85739,
+ },
+ conduits = {
+
+ },
+ covenants = {
+ condemn = 317485,
+ condemnMassacre = 330325,
+ },
+ debuffs = {
+ championsMight = 386286,
+ markedForExecution = 445584,
+ odynsFuryTormentMh = 385060,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ armoredToTheTeeth = 384124,
+ ashenJuggernaut = 392536,
+ avatar = 107574,
+ avatarOfTheStorm = 437134,
+ barbaricTraining = 390674,
+ berserkerStance = 386196,
+ berserkersTorment = 390123,
+ bladestorm = 227847,
+ bloodborne = 385703,
+ bloodcraze = 393950,
+ bloodthirst = 23881,
+ brutalFinish = 446085,
+ burstOfPower = 437118,
+ coldSteelHotBlood = 383959,
+ crashingThunder = 436707,
+ criticalThinking = 383297,
+ cruelty = 392931,
+ crushingForce = 382764,
+ cullingCyclone = 444778,
+ dancingBlades = 391683,
+ deathDrive = 444770,
+ deftExperience = 383295,
+ depthsOfInsanity = 383922,
+ -- dragonRoar = 118000,
+ dualWieldSpecialization = 382900,
+ enduranceTraining = 391997,
+ enragedRegeneration = 184364,
+ fierceFollowthrough = 444773,
+ flashingSkies = 437079,
+ focusInChaos = 383486,
+ frenziedEnrage = 383848,
+ frenzy = 335077,
+ freshMeat = 215568,
+ frothingBerserker = 215571,
+ -- furiousCharge = 202224,
+ gatheringClouds = 436201,
+ groundCurrent = 436148,
+ hackAndSlash = 383877,
+ imminentDemise = 444769,
+ improvedBloodthirst = 383852,
+ improvedExecute = 316402,
+ improvedRagingBlow = 383854,
+ improvedWhirlwind = 12950,
+ invigoratingFury = 383468,
+ keepYourFeetOnTheGround = 438590,
+ lightningStrikes = 434969,
+ massacre = 206315,
+ meatCleaver = 280392,
+ odynsFury = 385059,
+ onslaught = 315720,
+ opportunist = 444774,
+ overwhelmingBlades = 444772,
+ powerfulEnrage = 440277,
+ ragingBlow = 85288,
+ rampage = 184367,
+ reapTheStorm = 444775,
+ recklessAbandon = 396749,
+ recklessness = 1719,
+ relentlessPursuit = 444776,
+ -- seethe = 335091,
+ showNoMercy = 444771,
+ -- siegebreaker = 280772,
+ singlemindedFury = 81099,
+ slaughteringStrikes = 388004,
+ slayersDominance = 444767,
+ slayersMalice = 444779,
+ snapInduction = 456270,
+ steadfastAsThePeaks = 434970,
+ stormBolts = 436162,
+ stormOfSteel = 382953,
+ stormShield = 438597,
+ strengthOfTheMountain = 437068,
+ suddenDeath = 280721,
+ swiftStrikes = 383459,
+ tenderize = 388933,
+ thorimsMight = 436152,
+ thunderBlast = 435607,
+ titansTorment = 390135,
+ titanicRage = 394329,
+ unbridledFerocity = 389603,
+ unhinged = 386628,
+ unrelentingOnslaught = 444780,
+ viciousAgility = 444777,
+ viciousContempt = 383885,
+ warMachine = 346002,
+ warpaint = 208154,
+ wrathAndFury = 392936,
+ },
+ traits = {
+ coldSteelHotBlood = 288080
+ }
+ },
+ -- Protection
+ [73] = {
+ abilities = {
+ demoralizingShout = 1160,
+ devastate = 20243,
+ focusedRage = 204488,
+ impendingVictory = 202168,
+ intercept = 198304,
+ lastStand = 12975,
+ ravager = 228920,
+ revenge = 6572,
+ shieldBlock = 2565,
+ shieldSlam = 23922,
+ shieldWall = 871,
+ shockwave = 46968,
+ thunderClap = 6343,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ avatar = 107574,
+ defensiveStance = 71,
+ lastStand = 12975,
+ revenge = 5302,
+ shieldBlock = 132404,
+ shieldWall = 871,
+ vengeanceFocusedRage = 202573,
+ vengeanceIgnorePain = 202574,
+ vengeanceRevenge = 202573,
+ victorious = 32216,
+ },
+ conduits = {
+
+ },
+ covenants = {
+ condemn = 317349,
+ },
+ debuffs = {
+ deepwoundsProt = 115767,
+ demoralizingShout = 1160,
+ thunderClap = 6343,
+ },
+ glyphs = {
+
+ },
+ talents = {
+ bestServedCold = 202560,
+ warMachine = 316733,
+ bolster = 280001,
+ boomingVoice = 202743,
+ cracklingThunder = 203201,
+ devastator = 236279,
+ dragonRoar = 118000,
+ heavyRepercussions = 203177,
+ indomitable = 202095,
+ intoTheFray = 202603,
+ menace = 275338,
+ neverSurrender = 202561,
+ punish = 275334,
+ ravager = 228920,
+ rumblingEarth = 275339,
+ unstoppableForce = 275336,
+ },
+ },
+ -- All
+ Shared = {
+ abilities = {
+ attack = 88163,
+ battleShout = 6673,
+ battleStance = 2457,
+ berserkerRage = 18499,
+ berserkerStance = 2458,
+ charge = 100,
+ cleave = 845,
+ commandingShout = 469,
+ defensiveStance = 71,
+ demoralizingBanner = 114203,
+ disarm = 676,
+ execute = 5308,
+ hamstring = 1715,
+ heroicLeap = 6544,
+ heroicStrike = 78,
+ heroicThrow = 57755,
+ intervene = 3411,
+ intimidatingShout = 5246,
+ mockingBanner = 114192,
+ pummel = 6552,
+ rallyingCry = 97462,
+ recklessness = 1719,
+ -- shieldBlock = 2565,
+ -- shieldSlam = 23922,
+ shatteringThrow = 64382,
+ shieldWall = 871,
+ skullBanner = 114207,
+ spellReflection = 23920,
+ sunderArmor = 7386,
+ taunt = 355,
+ throw = 122475,
+ thunderClap = 6343,
+ victoryRush = 34428,
+ -- whirlwind = 1680,
+ },
+ animas = {
+ -- bloodstainedWhetstone = 322036,
+ -- hurricaneHeart = 329122,
+ -- championsDecree = 322054,
+ -- periaptOfFuror = 322049,
+ -- pleonexianCommand = 329165,
+ -- rageMote = 322046,
+ -- weatheredRunestone = 349916,
+ -- singingStones = 329489,
+ -- singingStonesOfIntimidation = 329484,
+ -- singingStonesOfUnnerving = 329488,
+ -- tarnishedMedallion = 348483,
+ -- thunderingRoar = 322093,
+ -- zovaalsWarbanner = 322027,
+ -- bladedBulwark = 322026,
+ -- fanOfLongswords = 322029,
+ -- misshapenMirror = 329268,
+ -- shriekingFlagon = 329275,
+ -- voraciousCullingBlade = 329213,
+ -- kyrianWarhelm = 314290,
+ -- scratchedKnife = 322039,
+ -- signetOfTormentedKings = 329345,
+ -- smolderingInertia = 322032,
+ -- umbralEarTrumpet = 329283,
+ -- warlordsResolve = 322051,
+ },
+ artifacts = {
+
+ },
+ buffs = {
+ battleShout = 6673,
+ -- berserkerRage = 18499,
+ bloodbath = 12292,
+ defensiveStance = 71,
+ enrage = 12880,
+ -- firstStrike = 325381,
+ -- fujiedasFury = 207776,
+ -- ignorePain = 190456,
+ -- outburst = 364010,
+ recklessness = 1719,
+ -- seeingRed = 364006,
+ skullBanner = 114207,
+ -- smolderingInertia = 322032,
+ -- spellReflection = 23920,
+ victorious = 32216,
+ },
+ conduits = {
+ -- viciousContempt = 337302,
+ -- destructiveReverberations = 339939,
+ },
+ covenants = {
+ -- ancientAftershock = 325886,
+ -- conquerorsBanner = 324143,
+ -- fleshcraft = 324631,
+ -- spearOfBastion = 307865,
+ },
+ debuffs = {
+ deepWounds = 115768,
+ disarm = 116095,
+ -- markOfFyralath = 414532,
+ },
+ glyphs = {
+
+ },
+ items = {
+ -- treacherousTransmitter = 221023,
+ },
+ runeforges = {
+ -- battlelord = 335274,
+ -- cadenceOfFujieda = 335555,
+ -- deathmaker = 335567,
+ -- elysianMight = 357996,
+ -- enduringBlow = 335458,
+ -- exploiter = 335451,
+ -- leaper = 335214,
+ -- misshapenMirror = 335253,
+ -- recklessDefence = 335582,
+ -- reprisal = 335718,
+ -- seismicReverberation = 335758,
+ -- signetOfTormentedKings = 335266,
+ -- sinfulSurge = 354131,
+ -- theWall = 335239,
+ -- thunderlord = 335229,
+ -- unhinged = 335282,
+ -- willOfTheBerserker = 335594,
+ -- unbreakableWill = 335629,
+ },
+ talents = {
+ avatar = 107574,
+ bladestorm = 46924,
+ bloodbath = 12292,
+ doubleTime = 103827,
+ disruptingShout = 102060,
+ dragonRoar = 118000,
+ enragedRegeneration = 55694,
+ impendingVictory = 103840,
+ juggernaut = 103826,
+ massSpellReflection = 114028,
+ piercingHowl = 12323,
+ safeguard = 114029,
+ secondWind = 29838,
+ shockwave = 46968,
+ staggeringShout = 107566,
+ stormBolt = 107570,
+ vigilance = 114030,
+ warbringer = 103828,
+ },
+ },
+ },
+ -- Global
+ Shared = {
+ Shared = {
+ abilities = {
+ autoAttack = 6603,
+ autoShot = 75,
+ giftOfTheNaaru = br.functions.spell:getRacial("Draenei"), --select(7, GetSpellInfo(GetSpellInfo(28880))),
+ global = 5176, -- Classic uses class-specific GCD spells, but we use a default here
+ -- latentArcana = 296971,
+ -- lightsJudgment = 255647,
+ quakingPalm = br.functions.spell:getRacial("Pandaren"),
+ racial = br.functions.spell:getRacial(),
+ shadowmeld = br.functions.spell:getRacial("NightElf"),
+ -- twoForms = 68996,
+ },
+ animas = {
+ -- scrollOfElchaver = 305006,
+ },
+ artifacts = {
+ -- artificialDamage = 226829,
+ -- artificialStamina = 211309,
+ -- concordanceOfTheLegionfall = 239042,
+ },
+ buffs = {
+ -- ancientHysteria = 90355,
+ -- battlePotionOfAgility = 279152,
+ -- battlePotionOfIntellect = 279151,
+ -- battlePotionOfStrength = 279153,
+ -- superiorBattlePotionOfAgility = 298146,
+ -- potionOfUnbridledFury = 300714,
+ -- blessingOfSacrifice = 6940,
+ -- greaterBlessingOfKings = 203538,
+ -- greaterBlessingOfWisdom = 203539,
+ -- battleScarredAugmentation = 270058, -- BfA Augment Rune Buff
+ -- blessingOfFreedom = 1044,
+ -- blessingOfProtection = 1022,
+ -- bloodLust = {
+ -- ancientHysteria = 90355,
+ -- bloodlust = 2825,
+ -- drumsOfRage = 146555,
+ -- -- drumsOfTheMaelstrom = 256740,
+ -- -- drumsOfTheMountain = 230935,
+ -- heroism = 32182,
+ -- -- netherwinds = 160452,
+ -- -- primalRage = 264667,
+ -- timewarp = 80353,
+ -- },
+ -- concordanceOfTheLegionfall = 239042,
+ -- defiledAugmentation = 224001, -- Lightforged Augment Rune buff
+ -- felFocus = 242551,
+ -- flashOfSpringBlossoms = 105689,
+ -- flaskOfTenThousandScars = 188035,
+ -- flaskOfTheCountlessArmies = 188034,
+ -- flaskOfTheSeventhDemon = 188033,
+ -- flaskOfTheWhisperedPact = 188031,
+ -- flaskOfTheCurrents = 251836,
+ -- flaskOfEndlessFathoms = 251837,
+ -- flaskOfTheVastHorizon = 251838,
+ -- flaskOfTheUndertow = 251839,
+ -- flayedWingToxin = 345546,
+ -- fruitfulMachinatins = 242623, -- Absorb Shield from Deceiver's Grand Design
+ -- mistcallerOcarina = 330067, -- SL Trinket with group buff
+ -- greaterFlaskOfEndlessFathoms = 298837,
+ -- greaterFlaskOfTheCurrents = 298836,
+ -- greaterFlaskOfTheUndertow = 298841,
+ -- greaterFlaskOfTheVastHorizon = 298839,
+ -- guidingHand = 242622, -- from The Deceiver's Grand Design
+ -- heroism = 32182,
+ -- ineffableTruth = 316801,
+ -- latentArcana = 296962,
+ -- netherwinds = 160452,
+ -- norgannonsForesight = 236373,
+ -- overchargeMana = 299624,
+ -- potionOfBurstingBlood = 265514,
+ -- potionOfFocusedResolve = 298317,
+ -- prolongedPower = 229206,
+ racial = br.functions.spell:getRacial(),
+ -- razorCoral = 303570, -- Crit Buff from Ashvane's Razor Coral
+ -- sephuz1 = 208051, -- the fulltime 10% movement, 2% haste buff
+ -- sephuz2 = 208052, -- the proc, 70% movement, 25% haste buff
+ -- sephuzCooldown = 226262, -- CD (30 seconds) for the proc
+ -- shadowmeld = 20580,
+ -- symbolOfHope = 64901,
+ -- timeWarp = 80353,
+ -- whispersOfInsanity = 176151,
+ -- cracklingTourmaline = 290372,
+ -- saphireofBrilliance = 290365,
+ -- vigorEngaged = 287916,
+ -- soulshape = 310143,
+ -- soulIgnition = 345251,
+ -- spectralFlaskOfPower = 307185,
+ -- elementalPotionOfPower = 371024,
+ -- elementalPotionOfUltimatePower = 371028,
+ -- phialOfGlacialFury = 373257,
+ -- phialOfTepidVersatility = 371172,
+ -- icedPhialOfCorruptingRage = 374000,
+ -- domineeringArrogance = 411661,
+ -- runeOfReorigination = 139120,
+ -- vicious = 148903,
+ -- virmensBite = 105697,
+ },
+ --TODO Add to API
+ itemEnchantments = {
+ --Dragonflight weapon Imbue's from inscription enchantments
+ --Quality of aura should always be GOLD,SILVER,COPPER
+ -- buzzingRune = { 6514, 6513, 6512 },
+ -- chirpingRune = { 6695, 6694, 6515 },
+ -- howlingRune = { 6518, 6517, 6516 },
+ -- hissingRune = { 6839, 6837, 6838 },
+ },
+ conduits = {
+
+ },
+ covenants = {
+ -- covenantAbility = 313347,
+ -- doorOfShadows = 300728,
+ -- fleshcraft = 324631,
+ -- soulshape = 310143,
+ -- flicker = 324701,
+ -- summonSteward = 324739,
+ },
+ debuffs = {
+ -- bloodOfTheEnemy = 297108,
+ -- concentratedFlame = 295368,
+ -- conductiveInk = 302565,
+ -- convert = 122740,
+ -- dampening = 110310,
+ -- eyeOfLeotheras = 206649,
+ -- necroticWound = 209858,
+ -- razorCoral = 303568, --304877,
+ -- repeatPerformance = 304409,
+ -- shiverVenom = 301624,
+ -- temptation = 234143,
+ -- eyeOfCorruption = 315161,
+ -- grandDelusions = 319695,
+ -- graspingTendrils = 315176,
+ -- creepingMadness = 313255,
+ -- fixate = 318078,
+ -- thirstForBlood = 266107,
+ -- mightyBash = 5211,
+ -- defiledGround = 314565,
+ -- vileCorruption = 314392,
+ -- timeToFeed = 162415, -- Oshir Iron Docks
+ -- cascadingTerror = 314478,
+ },
+ essences = {
+ },
+ },
+ },
+}
diff --git a/System/Lists/Items.lua b/System/Lists/Items.lua
index 74fd8369df..d73b2cc2d7 100644
--- a/System/Lists/Items.lua
+++ b/System/Lists/Items.lua
@@ -1,20 +1,5 @@
local _, br = ...
if br.lists == nil then br.lists = {} end
-function br.getHeirloomNeck()
- local necks = {
- eternalAmuletOfTheRedeemed = 122663,
- eternalEmberfuryTalisman = 122667,
- eternalHorizonChoker = 122664,
- eternalTalismanOfEvasion = 122662,
- eternalWillOfTheMartyr = 122668,
- eternalWovenIvyNecklace = 122666,
- manariTrainingAmulet = 153130,
- }
- for _, v in pairs(necks) do
- if br.hasEquiped(v, 2) then return v end
- end
- return 0
-end
br.lists.items = {
-- Dragon Isle Food buffs
@@ -57,6 +42,7 @@ br.lists.items = {
defiledAugmentRune = 140587,
lightforgedAugmentRune = 153023, -- 7.3 augment rune item
-- Flasks
+ flaskOfSpringBlossoms = 76084,
flaskOfTenThousandScars = 127850,
flaskOfTheCountlessArmies = 127849,
flaskOfTheSeventhDemon = 127848,
@@ -75,8 +61,10 @@ br.lists.items = {
greaterFlaskOfTheVastHorizon = 168653,
-- Healing Items
phialOfSerenity = 177278,
+ abyssalHealingPotion = 169451, -- non-enUS locale healing pot
+ coastalHealingPotion = 171267, -- non-enUS locale healing pot (SL)
-- Healthstones
- healthstone = 5512,
+ healthstone = {5512, 19004, 19005, 5511, 19006, 19007, 19008, 19009, 5509, 5510, 19010, 19011, 19012, 9412, 19013, 22104, 22105, 22103}, -- rank 1-6 (Minor → Master)
legionHealthstone = 129196,
-- Heirlooms
eternalAmuletOfTheRedeemed = 122663,
@@ -85,7 +73,7 @@ br.lists.items = {
eternalTalismanOfEvasion = 122662,
eternalWillOfTheMartyr = 122668,
eternalWovenIvyNecklace = 122666,
- heirloomNeck = br.getHeirloomNeck(),
+ -- heirloomNeck is set after the table so getHeirloomNeck() can read the individual neck IDs above.
manariTrainingAmulet = 153130,
touchOfTheVoid = 128318,
-- Potions
@@ -95,8 +83,8 @@ br.lists.items = {
potionOfBurstingBlood = 152560,
potionOfProlongedPower = 142117,
potionOfTheOldWar = 127844,
+ virmensBite = 76089,
-- 8.2 Potions
- abyssalHealingPotion = 169451,
potionOfUnbridledFury = 169299, -- DPS Potion
potionOfEmpoweredProximity = 168529, -- DPS Potion (AoE)
potionOfFocusedResolve = 168506, -- Crit Damage Potion
@@ -222,8 +210,6 @@ br.lists.items = {
icedPhialOfCorruptingRageQualities = { 204654, 191329, 204653, 191328, 204652, 191327 },
phialOfTepidVersatilityQualities = { 204666, 191341, 204665, 191340, 204664, 191339 },
dreamwalkersHealingPotion = { 207023, 207022, 207021 }
-
-
-
-
}
+-- Computed after the table so getHeirloomNeck() can safely read the individual neck entries above.
+br.lists.items.heirloomNeck = br.functions.item:getHeirloomNeck()
\ No newline at end of file
diff --git a/System/Lists/LineOfSight.lua b/System/Lists/LineOfSight.lua
index 294ea4df80..e7a47c1efc 100644
--- a/System/Lists/LineOfSight.lua
+++ b/System/Lists/LineOfSight.lua
@@ -3,8 +3,326 @@ if br.lists == nil then
br.lists = {}
end
br.lists.los = {
- [56754] = true, -- Azure Serpent (Shado'pan Monestary)
- [56895] = true, -- Weak Spot - Raigon (Gate of the Setting Sun)
+ --[[===========================]]
+ --[[ CLASSIC / VANILLA (60) ]]
+ --[[===========================]]
+
+ -- Stratholme
+ [10811] = true, -- Archimondethe Dreadlord (elevated platform)
+ [10558] = true, -- Hearthsinger Forresten (zigzag corridors)
+
+ -- Scholomance (Vanilla)
+ [10506] = true, -- Kirtonos the Herald (corner room)
+ [10433] = true, -- Marduk Blackpool (pillars)
+
+ -- Blackrock Depths
+ [9019] = true, -- Emperor Dagran Thaurissan (throne elevation)
+ [9025] = true, -- Lord Roccor (pillar room)
+
+ -- Upper Blackrock Spire
+ [10363] = true, -- General Drakkisath (elevated throne)
+ [10429] = true, -- Warchief Rend Blackhand (ledges)
+
+ -- Sunken Temple
+ [5721] = true, -- Dreamscythe (dragon pit)
+ [8443] = true, -- Avatar of Hakkar (room corners)
+
+ -- Maraudon
+ [13282] = true, -- Noxxion (gas clouds block LoS)
+ [12201] = true, -- Princess Theradras (waterfall cave)
+
+ -- Zul'Farrak
+ [7267] = true, -- Chief Ukorz Sandscalp (pyramid top)
+ [7273] = true, -- Gahz'rilla (water pool)
+
+ -- Uldaman
+ [6906] = true, -- Baelog (dwarf trio room)
+ [2748] = true, -- Archaedas (titan vault, pillars)
+
+ -- Molten Core
+ [11982] = true, -- Magmadar (pillars in room)
+
+ -- Blackwing Lair
+ [14020] = true, -- Chromaggus (winding tunnel)
+ [11583] = true, -- Nefarian (elevated platform)
+
+ -- Ahn'Qiraj 40
+ [15263] = true, -- Prophet Skeram (clones teleport)
+ [15510] = true, -- Fankriss the Unyielding (tunnel segments)
+
+ --[[===========================]]
+ --[[ BURNING CRUSADE (70) ]]
+ --[[===========================]]
+
+ -- Shadow Labyrinth
+ [18667] = true, -- Blackheart the Inciter (circular room)
+ [18708] = true, -- Murmur (sound waves around pillars)
+
+ -- Shattered Halls
+ [17711] = true, -- Warbringer O'mrogg (two-headed, moves erratically)
+
+ -- Botanica
+ [17976] = true, -- Commander Sarannis (tree platform)
+ [18422] = true, -- High Botanist Freywinn (garden maze)
+
+ -- Magisters' Terrace
+ [24723] = true, -- Selin Fireheart (crystal pillars)
+ [24560] = true, -- Priestess Delrissa (chaotic arena)
+
+ -- Mana-Tombs
+ [18341] = true, -- Pandemonius (void zones)
+ [22930] = true, -- Yor (ethereal platform)
+
+ -- Auchenai Crypts
+ [18373] = true, -- Exarch Maladaar (sarcophagus room)
+
+ -- Sethekk Halls
+ [18473] = true, -- Talon King Ikiss (bird room, blinks)
+ [23035] = true, -- Anzu (Raven God, summon boss)
+
+ -- Old Hillsbrad Foothills
+ [17848] = true, -- Lieutenant Drake (town buildings)
+ [18096] = true, -- Epoch Hunter (caves)
+
+ -- Black Morass
+ [17879] = true, -- Chrono Lord Deja (portal room)
+ [17881] = true, -- Aeonus (shield generators)
+
+ -- Mechanar
+ [19219] = true, -- Mechano-Lord Capacitus (machinery)
+ [19221] = true, -- Nethermancer Sepethrea (elevated platforms)
+
+ -- Blood Furnace
+ [17381] = true, -- The Maker (torture chamber)
+ [17380] = true, -- Broggok (poison cloud room)
+
+ -- Hellfire Ramparts
+ [17306] = true, -- Watchkeeper Gargolmar (gauntlet)
+ [17537] = true, -- Vazruden the Herald (flying wyvern)
+
+ -- Slave Pens
+ [17941] = true, -- Mennu the Betrayer (bog water)
+ [17991] = true, -- Rokmar the Crackler (bog lord)
+
+ -- Underbog
+ [17770] = true, -- Hungarfen (mushroom forest)
+ [17826] = true, -- Swamplord Musel'ek (pet mechanics)
+
+ -- Steamvault
+ [17797] = true, -- Hydromancer Thespia (water rises)
+ [17882] = true, -- Warlord Kalithresh (water mechanics)
+
+ -- Karazhan
+ [15688] = true, -- Prince Malchezaar (balcony height issues)
+
+ -- Black Temple
+ [22841] = true, -- Shade of Akama (phasing issues)
+ [22917] = true, -- Illidan Stormrage (elevation changes)
+
+ --[[===========================]]
+ --[[ WRATH OF THE LICH KING (80) ]]
+ --[[===========================]]
+
+ -- Utgarde Keep
+ [23953] = true, -- Prince Keleseth (frost tomb pillars)
+ [24201] = true, -- Skarvald and Dalronn (two bosses, elevation)
+
+ -- Nexus
+ [26731] = true, -- Grand Magus Telestra (splits, teleports)
+ [26723] = true, -- Keristrasza (crystals in room)
+
+ -- Azjol-Nerub
+ [28684] = true, -- Krik'thir the Gatewatcher (web bridges)
+ [29120] = true, -- Anub'arak (spikes and ground phases)
+
+ -- Drak'Tharon Keep
+ [26630] = true, -- Trollgore (corner room, corpse piles)
+ [27483] = true, -- King Dred (raptor den corners)
+
+ -- Violet Hold
+ [29313] = true, -- Ichoron (water form, hard to see)
+ [31134] = true, -- Cyanigosa (portal spawn)
+
+ -- Gundrak
+ [29305] = true, -- Slad'ran (snake form, coils)
+ [29932] = true, -- Eck the Ferocious (corner room)
+
+ -- Halls of Stone
+ [27975] = true, -- Sjonnir the Ironshaper (central forge)
+ [27977] = true, -- Tribunal of Ages event (arena with pillars)
+
+ -- Halls of Lightning
+ [28586] = true, -- General Bjarngrim (elevated platform)
+ [28923] = true, -- Loken (circular room, lightning nova)
+
+ -- Oculus
+ [27447] = true, -- Mage-Lord Urom (teleports between platforms)
+ [27656] = true, -- Ley-Guardian Eregos (flying ring platforms)
+
+ -- Culling of Stratholme
+ [26532] = true, -- Chrono-Lord Epoch (time rift phases)
+ [32273] = true, -- Infinite Corruptor (gauntlet event)
+
+ -- Utgarde Pinnacle
+ [26687] = true, -- Gortok Palehoof (animal transformations)
+ [26861] = true, -- King Ymiron (boat room, stances)
+
+ -- Trial of the Champion
+ [35451] = true, -- The Black Knight (mounted phases)
+ [35617] = true, -- Faction Champions (arena chaos)
+
+ -- Pit of Saron
+ [36494] = true, -- Forgemaster Garfrost (icy corridor)
+ [36658] = true, -- Scourgelord Tyrannus (mounted, flying overlook)
+
+ -- Forge of Souls
+ [36502] = true, -- Devourer of Souls (phantom form)
+ [36497] = true, -- Bronjahm (soul fragments)
+
+ -- Halls of Reflection
+ [38112] = true, -- The Lich King (gauntlet chase, pillars)
+ [37226] = true, -- Falric (corner spawns)
+
+ -- Naxxramas
+ [15990] = true, -- Kel'Thuzad (elevated throne)
+
+ -- Ulduar
+ [33186] = true, -- Razorscale (flying/grounded transitions)
+ [33515] = true, -- Auriaya (Sanctum stairs)
+ [33993] = true, -- Yogg-Saron (brain room portals)
+
+ -- Icecrown Citadel
+ [36855] = true, -- Lady Deathwhisper (stairs elevation)
+ [37813] = true, -- Deathbringer Saurfang (platform center vs edges)
+ [36789] = true, -- Valithria Dreamwalker (portals)
+ [36853] = true, -- Sindragosa (blocks of ice LoS)
+ [36597] = true, -- The Lich King (platform transitions)
+
+ --[[===========================]]
+ --[[ CATACLYSM (85) ]]
+ --[[===========================]]
+
+ -- Throne of the Tides
+ [40586] = true, -- Lady Naz'jar (waterspout phases)
+ [40765] = true, -- Commander Ulthok (tentacles and fissures)
+ [40792] = true, -- Neptulon (water elementals, tidal surge)
+
+ -- Blackrock Caverns
+ [39679] = true, -- Corla, Herald of Twilight (beams and pillars)
+ [39625] = true, -- General Umbriss (ground ruptures)
+ [39698] = true, -- Karsh Steelbender (lava pillars)
+
+ -- Vortex Pinnacle
+ [43878] = true, -- Grand Vizier Ertan (cyclone platforms)
+ [43873] = true, -- Altairus (wind changes, elevation)
+ [43875] = true, -- Asaad (static cling, triangle platforms)
+
+ -- Stonecore
+ [43438] = true, -- Corborus (burrow mechanic, underground)
+ [43214] = true, -- Slabhide (stalactite falls, pillars)
+ [43391] = true, -- High Priestess Azil (gravity wells, pillar room)
+
+ -- Lost City of the Tol'vir
+ [44577] = true, -- General Husam (land mines, square arena)
+ [44819] = true, -- Siamat (servant of Asaad, wind phase)
+ [45007] = true, -- Lockmaw (chain breakage)
+
+ -- Halls of Origination
+ [39428] = true, -- Temple Guardian Anhuur (snake pit drop)
+ [39587] = true, -- Isiset (astral rain, sphere room)
+ [39788] = true, -- Anraphet (omega stance, H-shaped room)
+ [39731] = true, -- Rajh (solar winds, sun platform)
+
+ -- Grim Batol
+ [40177] = true, -- Forgemaster Throngus (dwarf forge, anvils)
+ [40319] = true, -- Drahga Shadowburner (dragon ride)
+ [40484] = true, -- Erudax (faceless spawns, shadow gale)
+
+ -- Deadmines (Heroic)
+ [47162] = true, -- Glubtok (lightning rod room)
+ [47296] = true, -- Helix Gearbreaker (gnome bomb room)
+ [47739] = true, -- "Captain" Cookie (cauldron room)
+
+ -- Shadowfang Keep (Heroic)
+ [46962] = true, -- Baron Ashbury (multi-level room)
+ [46963] = true, -- Baron Silverlaine (staircase fights)
+ [4278] = true, -- Commander Springvale (courtyard)
+
+ -- Zul'Aman (Heroic)
+ [23574] = true, -- Akil'zon (eagle platform, storms)
+ [23576] = true, -- Nalorakk (bear/troll form, columns)
+ [23578] = true, -- Jan'alai (bird boss, fire bombs)
+ [24239] = true, -- Hex Lord Malacrass (spirit bolts)
+
+ -- Zul'Gurub (Heroic)
+ [52258] = true, -- Gri'lek (raptor boss, pursuit)
+ [52271] = true, -- Hazza'rah (cobra boss, totems)
+ [52269] = true, -- Renataki (tiger boss, ambush)
+ [52286] = true, -- Wushoolay (panther boss, lightning)
+
+ -- End Time
+ [54431] = true, -- Echo of Baine (totem ring)
+ [54445] = true, -- Echo of Jaina (frost pillars)
+ [54544] = true, -- Echo of Sylvanas (platform jumps)
+
+ -- Well of Eternity
+ [54853] = true, -- Peroth'arn (invisible phases)
+ [54968] = true, -- Queen Azshara (puppet strings, large arena)
+ [54969] = true, -- Mannoroth (portal platform, infernal spawns)
+
+ -- Hour of Twilight
+ [54590] = true, -- Arcurion (ice tomb, crystals)
+ [54938] = true, -- Asira Dawnslayer (smoke bomb vanish)
+ [54432] = true, -- Archbishop Benedictus (holy/shadow split, pillars)
+
+ -- Blackwing Descent
+ [41570] = true, -- Magmaw (head up/down)
+ [43296] = true, -- Chimaeron (frenzy movement)
+
+ -- Bastion of Twilight
+ [43735] = true, -- Elementium Monstrosity (Sinestra add, elevated spawn)
+
+ -- Firelands
+ [52498] = true, -- Beth'tilac (ceiling/ground transitions)
+ [52530] = true, -- Alysrazor (flying rings)
+
+ -- Dragon Soul
+ [55294] = true, -- Ultraxion (deck transitions)
+ [56427] = true, -- Warmaster Blackhorn (ship deck height)
+ [53879] = true, -- Spine of Deathwing (rolling barrel mechanic)
+
+ --[[===========================]]
+ --[[ MISTS OF PANDARIA CLASSIC ]]
+ --[[===========================]]
+
+ -- Shado-Pan Monastery
+ [56754] = true, -- Azure Serpent
+ [59802] = true, -- Master Snowdrift (phase transitions often have LoS issues)
+
+ -- Gate of the Setting Sun
+ [56895] = true, -- Weak Spot - Raigonn
+ [59544] = true, -- Commander Ri'mok
+
+ -- Temple of the Jade Serpent
+ [59051] = true, -- Corrupt Living Water (frequently behind pillars)
+
+ -- Mogu'shan Palace
+ [61398] = true, -- Gekkan (arena fight with LoS issues)
+
+ -- Throne of Thunder (Raid)
+ [69427] = true, -- Waterspout (Council of Elders, water adds)
+ [68078] = true, -- Lei Shen (elevated platform)
+ [68476] = true, -- Horridon (gate mechanics)
+
+ -- Siege of Orgrimmar (Raid)
+ [71734] = true, -- Sha of Pride (phase transitions)
+ [71466] = true, -- Iron Juggernaut (drill phase LoS)
+ [71865] = true, -- Garrosh Hellscream (platform transitions)
+
+ --[[===========================]]
+ --[[ LATER EXPANSIONS (KEEP) ]]
+ --[[===========================]]
+
[76585] = true, -- Ragewing
[77692] = true, -- Kromog
[77182] = true, -- Oregorger
diff --git a/System/Lists/NoTouchUnits.lua b/System/Lists/NoTouchUnits.lua
index 162359b97e..fbc3399af3 100644
--- a/System/Lists/NoTouchUnits.lua
+++ b/System/Lists/NoTouchUnits.lua
@@ -2,12 +2,293 @@ local _, br = ...
if br.lists == nil then
br.lists = {}
end
--- No Touch Units = List of units that we should not attack for any reason
+-- No Touch Units: hard "do not attack" rules (invulnerability/phase/reflect/encounter mechanics).
+-- This list is enforced by enemiesEngineFunctions:isSafeToAttack() and should REMOVE/INVALIDATE targets.
+-- If a unit is merely taking reduced damage (but still attackable), prefer adding it to ShieldUnits.lua
+-- so it is only deprioritized when "Avoid Shields" is enabled.
br.lists.noTouchUnits = {
- -- Jade Temple
- { unitID = 56448, buff = 106062 }, -- Wise Mari with Bubble
- --Shadopan Monastery
+ --[[===========================]]
+ --[[ CLASSIC / VANILLA (60) ]]
+ --[[===========================]]
+
+ -- Stratholme
+ { unitID = 10811, buff = 17086 }, -- Archimondethe Dreadlord Carrion Swarm immunity
+ { unitID = 10558, buff = 16798 }, -- Hearthsinger Forresten Fear immunity
+
+ -- Blackrock Depths
+ { unitID = 9019, buff = 13704 }, -- Emperor Dagran Thaurissan Avatar of Flame
+
+ -- Upper Blackrock Spire
+ { unitID = 10363, buff = 15732 }, -- General Drakkisath Conflagration immunity
+
+ -- Sunken Temple
+ { unitID = 5721, buff = 12480 }, -- Dreamscythe with healing immunity phase
+
+ -- Maraudon
+ { unitID = 13282, buff = 21909 }, -- Noxxion with Toxic Volley immunity
+ { unitID = 12201, buff = 21869 }, -- Princess Theradras with Dust Field
+
+ -- Zul'Farrak
+ { unitID = 7273, buff = 11641 }, -- Gahz'rilla with Frenzy
+
+ -- Molten Core
+ { unitID = 12018, buff = 19595 }, -- Majordomo Executus with Shield of Flame
+
+ -- Blackwing Lair
+ { unitID = 11583, buff = 23414 }, -- Nefarian with Shadow Flame immunity
+
+ -- Ahn'Qiraj 40
+ { unitID = 15589, buff = 26525 }, -- Twin Emperors with Twin Teleport immunity
+ { unitID = 15587, buff = 26580 }, -- Twin Emperors physical immunity
+
+ -- Naxxramas (Classic)
+ { unitID = 15990, buff = 28732 }, -- Kel'Thuzad with Frost Blast (don't break early)
+ { unitID = 16061, buff = 28776 }, -- Four Horsemen Immune
+
+ --[[===========================]]
+ --[[ BURNING CRUSADE (70) ]]
+ --[[===========================]]
+
+ -- Shadow Labyrinth
+ { unitID = 18667, buff = 33676 }, -- Blackheart the Inciter Incite Chaos
+
+ -- Shattered Halls
+ { unitID = 20923, buff = 31901 }, -- Blood Guard Porung Shield Wall
+
+ -- Botanica
+ { unitID = 17976, buff = 34670 }, -- Commander Sarannis Arcane Resonance
+
+ -- Magisters' Terrace
+ { unitID = 24723, buff = 46757 }, -- Selin Fireheart Fel Crystal immunity
+
+ -- Mana-Tombs
+ { unitID = 18341, buff = 32361 }, -- Pandemonius with Void Blast
+
+ -- Auchenai Crypts
+ { unitID = 18373, buff = 32424 }, -- Exarch Maladaar with Avatar of the Martyred
+
+ -- Sethekk Halls
+ { unitID = 18473, buff = 32690 }, -- Talon King Ikiss with Blink immunity
+
+ -- Black Morass
+ { unitID = 17879, buff = 31473 }, -- Chrono Lord Deja with Magnetic Pull immunity
+
+ -- Mechanar
+ { unitID = 19219, buff = 35159 }, -- Mechano-Lord Capacitus with Reflective Damage Shield
+
+ -- Blood Furnace
+ { unitID = 17381, buff = 30923 }, -- The Maker with Domination mind control
+
+ -- Slave Pens
+ { unitID = 17941, buff = 31946 }, -- Mennu the Betrayer with Corrupted Nova
+
+ -- Karazhan
+ { unitID = 15688, buff = 29506 }, -- Prince Malchezaar Enfeeble immunity
+
+ -- Black Temple
+ { unitID = 22917, buff = 41252 }, -- Illidan Stormrage Demon Form transition
+ { unitID = 22948, buff = 41083 }, -- Reliquary of Souls Deaden immunity
+
+ -- Sunwell Plateau
+ { unitID = 25315, buff = 45996 }, -- Kil'jaeden with Shield Orb immunity
+ { unitID = 25840, buff = 46102 }, -- M'uru Entropius transformation
+
+ --[[===========================]]
+ --[[ WRATH OF THE LICH KING (80) ]]
+ --[[===========================]]
+
+ -- Ahn'kahet: The Old Kingdom
+ { unitID = 29309, buff = 56153 }, -- Elder Nadox Guardian Aura immunity (from Ahn'kahar Guardian)
+
+ -- Violet Hold
+ { unitID = 31134, buff = 58040 }, -- Cyanigosa Arcane Vacuum immunity
+
+ -- Halls of Lightning
+ { unitID = 28586, buff = 52770 }, -- General Bjarngrim Defensive Stance
+ { unitID = 28923, buff = 52770 }, -- Loken Lightning Nova immunity
+
+ -- Culling of Stratholme
+ { unitID = 26532, buff = 52550 }, -- Chrono-Lord Epoch Time Warp immunity
+ { unitID = 32273, buff = 58817 }, -- Infinite Corruptor Dark Channeling
+
+ -- Pit of Saron
+ { unitID = 36494, buff = 70292 }, -- Forgemaster Garfrost Permafrost immunity
+ { unitID = 36658, buff = 69167 }, -- Scourgelord Tyrannus Unholy Power
+
+ -- Halls of Reflection
+ { unitID = 38112, buff = 72198 }, -- Lich King Remorseless Winter immunity
+
+ -- Naxxramas (Wrath)
+ { unitID = 15990, buff = 28732 }, -- Kel'Thuzad Frost Blast immunity
+ { unitID = 16061, buff = 28776 }, -- Four Horsemen immunity shield
+
+ -- Ulduar
+ { unitID = 33186, buff = 64320 }, -- Razorscale Devouring Flame
+ { unitID = 33993, buff = 64474 }, -- Yogg-Saron Weakened immunity phase
+ { unitID = 33271, buff = 63276 }, -- General Vezax Shadow Crash immunity
+
+ -- Trial of the Crusader
+ { unitID = 34497, buff = 66237 }, -- Faction Champions Bladestorm immunity
+ { unitID = 34564, buff = 68154 }, -- Anub'arak Submerge immunity
+
+ -- Icecrown Citadel
+ { unitID = 36855, buff = 70447 }, -- Lady Deathwhisper Mana Barrier (phase 1)
+ { unitID = 37813, buff = 69762 }, -- Deathbringer Saurfang Rune of Blood immunity
+ { unitID = 36626, buff = 70126 }, -- Festergut Gastric Bloat immunity
+ { unitID = 37025, buff = 72257 }, -- Blood Prince Council Invocation of Blood immunity
+ { unitID = 36853, buff = 72350 }, -- Sindragosa Frost Beacon (don't break early)
+ { unitID = 36597, buff = 70541 }, -- The Lich King Remorseless Winter immunity
+
+ --[[===========================]]
+ --[[ CATACLYSM (85) ]]
+ --[[===========================]]
+
+ -- Throne of the Tides
+ { unitID = 40586, buff = 76047 }, -- Lady Naz'jar Water Shield
+ { unitID = 40765, buff = 76815 }, -- Commander Ulthok Dark Fissure immunity
+
+ -- Vortex Pinnacle
+ { unitID = 43878, buff = 87761 }, -- Grand Vizier Ertan Cyclone Shield
+ { unitID = 43875, buff = 88282 }, -- Asaad Supremacy of the Storm
+
+ -- Stonecore
+ { unitID = 43438, buff = 92633 }, -- High Priestess Azil Gravity Well immunity
+ { unitID = 43214, buff = 86855 }, -- Slabhide Lava Fissure immunity
+
+ -- Grim Batol
+ { unitID = 40177, buff = 76634 }, -- Forgemaster Throngus Disorienting Roar
+ { unitID = 40319, buff = 75890 }, -- Drahga Shadowburner Burning Shadowbolt immunity
+
+ -- Blackrock Caverns
+ { unitID = 39679, buff = 75850 }, -- Corla Herald of Twilight Evolution shield
+
+ -- Lost City of Tol'vir
+ { unitID = 44819, buff = 83790 }, -- Siamat Gathered Storm immunity
+
+ -- Halls of Origination
+ { unitID = 39732, buff = 76394 }, -- Setesh Shield of the Ancestors immunity
+
+ -- Deadmines (Heroic)
+ { unitID = 49671, buff = 92620 }, -- Vanessa VanCleef Nightmares immunity phase
+
+ -- Shadowfang Keep (Heroic)
+ { unitID = 4278, buff = 93685 }, -- Commander Springvale Shield of the Perfidious
+
+ -- End Time
+ { unitID = 54432, buff = 102149 }, -- Echo of Tyrande Tears of Elune immunity
+
+ -- Well of Eternity
+ { unitID = 55085, buff = 105496 }, -- Peroth'arn Fel Flames immunity
+
+ -- Zul'Aman (Heroic)
+ { unitID = 23574, buff = 42471 }, -- Akil'zon Electrical Storm immunity
+ { unitID = 24239, buff = 43303 }, -- Hex Lord Malacrass Spirit Bolts immunity
+
+ -- Zul'Gurub (Heroic)
+ { unitID = 52258, buff = 96776 }, -- Gri'lek Pursuit immunity
+ { unitID = 52271, buff = 96914 }, -- Hazza'rah Chain Lightning immunity
+
+ -- Hour of Twilight
+ { unitID = 54968, buff = 103151 }, -- Archbishop Benedictus Purifying Light immunity
+ { unitID = 54432, buff = 103414 }, -- Benedictus Twilight Shear immunity
+
+ -- Blackwing Descent
+ { unitID = 41570, buff = 79339 }, -- Magmaw Lava Pillar immunity
+ { unitID = 41442, buff = 77679 }, -- Atramedes Sonar Bomb shield
+ { unitID = 43687, buff = 88835 }, -- Maloriak Phase transition shield
+
+ -- Bastion of Twilight
+ { unitID = 45993, buff = 83718 }, -- Halfus Dragon immunity shield
+ { unitID = 45992, buff = 92173 }, -- Valiona Blackout immunity
+
+ -- Throne of the Four Winds
+ { unitID = 46753, buff = 89668 }, -- Al'Akir Wind Burst immunity
+
+ -- Firelands
+ { unitID = 52498, buff = 100456 }, -- Beth'tilac Widow's Kiss (on ceiling)
+ { unitID = 53691, buff = 101410 }, -- Shannox Frenzy immunity
+ { unitID = 52530, buff = 99516 }, -- Alysrazor Blazing Power immunity
+
+ -- Dragon Soul
+ { unitID = 55294, buff = 109476 }, -- Ultraxion Hour of Twilight immunity
+ { unitID = 56427, buff = 106794 }, -- Warmaster Blackhorn Deck Fire immunity
+ { unitID = 56846, buff = 105479 }, -- Spine of Deathwing Armor immunity
+
+ --[[===========================]]
+ --[[ MISTS OF PANDARIA CLASSIC ]]
+ --[[===========================]]
+
+ -- Temple of the Jade Serpent
+ { unitID = 56448, buff = 106062 }, -- Wise Mari with Water Shield bubble
+ { unitID = 56765, buff = 106113 }, -- Sha of Doubt phase transition
+
+ -- Stormstout Brewery
+ { unitID = 56637, buff = 114548 }, -- Ook-Ook Going Bananas immunity
+ { unitID = 59479, buff = 115003 }, -- Yan-Zhu Wall of Suds
+
+ -- Mogu'shan Palace
+ { unitID = 61567, buff = 120201 }, -- Xin the Weaponmaster Blade Trap
+ { unitID = 61398, buff = 119946 }, -- Gekkan Reckless Inspiration
+
+ -- Shado-Pan Monastery
{ unitID = 56747, buff = 110945 }, -- Gu Cloudstrike with Charging Soul
+ { unitID = 56747, buff = 110852 }, -- Gu Cloudstrike phase immunity
+ { unitID = 56541, buff = 106062 }, -- Master Snowdrift Fists of Fury
+
+ -- Gate of the Setting Sun
+ { unitID = 56636, buff = 107120 }, -- Commander Ri'mok Viscous Fluid
+ { unitID = 56877, buff = 107268 }, -- Saboteur Kip'tilak Sabotage phase
+
+ -- Siege of Niuzao Temple
+ { unitID = 61567, buff = 121282 }, -- Vizier Jin'bak Detonate
+ { unitID = 62205, buff = 119073 }, -- Wing Leader Ner'onok Gusting Winds
+
+ -- Scarlet Halls
+ { unitID = 59303, buff = 113395 }, -- Armsmaster Harlan Bladestorm
+
+ -- Scarlet Monastery
+ { unitID = 3976, buff = 116266 }, -- Commander Mograine Divine Shield
+ { unitID = 60033, buff = 115458 }, -- Thalnos the Soulrender Spirit Gale
+
+ -- Scholomance
+ { unitID = 59153, buff = 111610 }, -- Instructor Chillheart Phylactery
+ { unitID = 58791, buff = 111606 }, -- Rattlegore Rusting shield
+
+ -- Mogu'shan Vaults (Raid)
+ { unitID = 60047, buff = 116829 }, -- Stone Guards Solid Stone
+ { unitID = 60009, buff = 116778 }, -- Feng the Accursed Epicenter
+
+ -- Heart of Fear (Raid)
+ { unitID = 62980, buff = 123081 }, -- Imperial Vizier Zor'lok Attenuation
+ { unitID = 62837, buff = 123600 }, -- Blade Lord Ta'yak Intensify
+ { unitID = 62543, buff = 123707 }, -- Empress Shek'zeer Dissonance Field
+
+ -- Terrace of Endless Spring (Raid)
+ { unitID = 62919, buff = 123244 }, -- Lei Shi Hide (vanish phase)
+ { unitID = 60410, buff = 119626 }, -- Tsulong day phase immunity
+
+ -- Throne of Thunder (Raid)
+ { unitID = 68476, buff = 136413 }, -- Horridon Dinomancy Shield
+ { unitID = 68397, buff = 134691 }, -- Council of Elders Blessed Loa Spirit
+ { unitID = 69427, buff = 137654 }, -- Tortos Spinning Shell
+ { unitID = 68036, buff = 134378 }, -- Ji-Kun Daedelian Wings (flight)
+ { unitID = 69712, buff = 140014 }, -- Durumu Light Spectrum
+ { unitID = 68078, buff = 136216 }, -- Lei Shen Static Shock
+
+ -- Siege of Orgrimmar (Raid)
+ { unitID = 71734, buff = 144351 }, -- Sha of Pride Corrupted Prison
+ { unitID = 71515, buff = 143574 }, -- Norushen Test realm
+ { unitID = 71479, buff = 143480 }, -- Malkorok Ancient Barrier
+ { unitID = 71466, buff = 143593 }, -- Iron Juggernaut Borer Drill
+ { unitID = 71858, buff = 142864 }, -- Thok Deafening Screech
+ { unitID = 71543, buff = 143502 }, -- Siegecrafter Blackfuse Protective Frenzy
+ { unitID = 71152, buff = 145071 }, -- Paragons Genetic Alteration
+ { unitID = 71865, buff = 144945 }, -- Garrosh Hellscream Iron Star phase
+
+ --[[===========================]]
+ --[[ LATER EXPANSIONS (KEEP) ]]
+ --[[===========================]]
+
-- Iron Docks
{ unitID = 87451, buff = 164504, spell = 164426 }, --Fleshrender Nok'gar, do not attack during defensive stance buff, Todo: Should stop when he cast 164504
{ unitID = 1, buff = 163689 }, -- Never attack Sanguine Sphere
@@ -68,7 +349,7 @@ br.lists.noTouchUnits = {
-- Tol Dagor
{ unitID = 133972 }, -- Cannon in Tol Dagor
{ unitID = 160652 }, -- Void Tentacle
-
+
-- Shadowlands
-- { unitID = 174773}, -- Spiteful Shade
-- Dungeons
diff --git a/System/Lists/Range.lua b/System/Lists/Range.lua
index d298d17a44..f6b20b6ce7 100644
--- a/System/Lists/Range.lua
+++ b/System/Lists/Range.lua
@@ -1,5 +1,8 @@
local _, br = ...
-br.rangeOrMelee = {
+br.lists.range = br.lists.range or {}
+local range = br.lists.range
+
+range.rangeOrMelee = {
[250] = "melee",
[251] = "melee",
[252] = "melee",
diff --git a/System/Lists/ShieldUnits.lua b/System/Lists/ShieldUnits.lua
index e8260e93fd..3cbf0baab7 100644
--- a/System/Lists/ShieldUnits.lua
+++ b/System/Lists/ShieldUnits.lua
@@ -2,8 +2,201 @@ local _, br = ...
if br.lists == nil then
br.lists = {}
end
--- Shield Units = shielding and levels, we should add coef as shield %
+-- Shield Units: "soft shields" that REDUCE incoming damage.
+-- These should DEPRIORITIZE targets (via coefficient penalty) when "Avoid Shields" is enabled.
+-- Do NOT use this list for invulnerability/phase/reflect mechanics that should hard-block targeting.
+-- Hard "do not attack" mechanics belong in NoTouchUnits.lua (and are enforced by isSafeToAttack()).
br.lists.shieldUnits = {
+ --[[===========================]]
+ --[[ CLASSIC / VANILLA (60) ]]
+ --[[===========================]]
+
+ -- Stratholme
+ [10811] = {coef = -75, buff = 17086}, -- Archimondethe Dreadlord Carrion Swarm - 75% reduction
+
+ -- Scholomance (Vanilla)
+ [10506] = {coef = -50, buff = 16799}, -- Kirtonos Shield - 50% reduction
+
+ -- Blackrock Depths
+ [9019] = {coef = -90, buff = 13704}, -- Emperor Dagran Avatar of Flame - 90% reduction
+
+ -- Upper Blackrock Spire
+ [10363] = {coef = -75, buff = 15732}, -- General Drakkisath Conflagration Shield - 75% reduction
+ [10814] = {coef = -50, buff = 15816}, -- Chromatic Elite Guard Shield - 50% reduction
+
+ -- Sunken Temple
+ [5721] = {coef = -50, buff = 12533}, -- Dreamscythe Dream Fog - 50% reduction
+
+ -- Maraudon
+ [13282] = {coef = -60, buff = 21909}, -- Noxxion Toxic Volley - 60% reduction
+
+ -- Molten Core
+ [12018] = {coef = -50, buff = 19595}, -- Majordomo Shield of Flame - 50% reduction
+
+ -- Blackwing Lair
+ [11583] = {coef = -75, buff = 23414}, -- Nefarian Shadow Flame - 75% reduction
+
+ --[[===========================]]
+ --[[ BURNING CRUSADE (70) ]]
+ --[[===========================]]
+
+ -- Shadow Labyrinth
+ [18667] = {coef = -75, buff = 33676}, -- Blackheart Incite Chaos - 75% reduction when charmed
+
+ -- Shattered Halls
+ [20923] = {coef = -90, buff = 31901}, -- Blood Guard Porung Shield Wall - 90% reduction
+
+ -- Mana-Tombs
+ [18341] = {coef = -50, buff = 32361}, -- Pandemonius Void Shield - 50% reduction
+
+ -- Auchenai Crypts
+ [18373] = {coef = -99, buff = 32424}, -- Exarch Maladaar Avatar Shield - 99% reduction
+
+ -- Sethekk Halls
+ [18473] = {coef = -50, buff = 32690}, -- Talon King Ikiss Shield - 50% reduction
+
+ -- Black Morass
+ [17879] = {coef = -75, buff = 31473}, -- Chrono Lord Time Shield - 75% reduction
+
+ -- Mechanar
+ [19219] = {coef = -100, buff = 35159}, -- Mechano-Lord Reflective Shield - 100% damage reflect
+
+ -- Blood Furnace
+ [17381] = {coef = -60, buff = 30923}, -- The Maker Domination - 60% reduction when controlling
+
+ -- Botanica
+ [17976] = {coef = -75, buff = 34670}, -- Commander Sarannis Arcane Resonance - 75% reduction
+
+ -- Slave Pens
+ [17941] = {coef = -50, buff = 31946}, -- Mennu Corrupted Nova - 50% reduction
+
+ -- Steamvault
+ [17797] = {coef = -60, buff = 33234}, -- Hydromancer Water Shield - 60% reduction
+
+ --[[===========================]]
+ --[[ WRATH OF THE LICH KING (80) ]]
+ --[[===========================]]
+
+ -- Violet Hold
+ [31134] = {coef = -75, buff = 58040}, -- Cyanigosa Arcane Shield - 75% reduction
+
+ -- Halls of Lightning
+ [28586] = {coef = -90, buff = 52770}, -- General Bjarngrim Defensive Stance - 90% reduction
+
+ -- Culling of Stratholme
+ [26532] = {coef = -75, buff = 52552}, -- Chrono-Lord Epoch Time Shield - 75% reduction
+
+ -- Pit of Saron
+ [36494] = {coef = -80, buff = 70326}, -- Forgemaster Garfrost Permafrost - 80% reduction
+
+ -- Halls of Reflection
+ [38112] = {coef = -99, buff = 72198}, -- Lich King Remorseless Winter - 99% reduction
+
+ -- Gundrak
+ [29305] = {coef = -50, buff = 55081}, -- Slad'ran Snake Wrap - 50% reduction
+
+ -- Drak'Tharon Keep
+ [26630] = {coef = -60, buff = 49405}, -- Trollgore Consume - 60% reduction
+
+ -- Utgarde Pinnacle
+ [26687] = {coef = -50, buff = 48894}, -- Gortok Palehoof Impale - 50% reduction frontal
+
+ -- Oculus
+ [27447] = {coef = -75, buff = 50240}, -- Mage-Lord Urom Time Bomb Shield - 75% reduction
+
+ -- Icecrown Citadel
+ [37890] = {coef = -100, buff = 70842}, -- Cult Adherent Dark Martyrdom - 100% absorption
+
+ --[[===========================]]
+ --[[ CATACLYSM (85) ]]
+ --[[===========================]]
+
+ -- Throne of the Tides
+ [40586] = {coef = -80, buff = 76047}, -- Lady Naz'jar Water Shield - 80% reduction
+ [40765] = {coef = -60, buff = 76815}, -- Commander Ulthok Dark Fissure - 60% reduction
+
+ -- Vortex Pinnacle
+ [43878] = {coef = -90, buff = 87761}, -- Grand Vizier Ertan Cyclone Shield - 90% reduction
+ [43875] = {coef = -75, buff = 88282}, -- Asaad Supremacy Shield - 75% reduction
+
+ -- Stonecore
+ [43438] = {coef = -80, buff = 92633}, -- High Priestess Azil Gravity Well - 80% reduction
+ [43214] = {coef = -70, buff = 86855}, -- Slabhide Lava Fissure - 70% reduction
+
+ -- Blackrock Caverns
+ [39679] = {coef = -75, buff = 75608}, -- Corla Dark Command - 75% reduction
+ [39625] = {coef = -60, buff = 74837}, -- General Umbriss Bleeding Wound - 60% reduction
+
+ -- Grim Batol
+ [40177] = {coef = -90, buff = 76634}, -- Forgemaster Throngus Disorienting Roar - 90% reduction
+ [40319] = {coef = -75, buff = 75889}, -- Drahga Burning Shadowbolt Shield - 75% reduction
+
+ -- Lost City of Tol'vir
+ [44577] = {coef = -50, buff = 83445}, -- General Husam Shockwave Shield - 50% reduction
+
+ -- Halls of Origination
+ [39428] = {coef = -99, buff = 75323}, -- Temple Guardian Shield - 99% reduction during phase
+ [39731] = {coef = -75, buff = 89878}, -- Rajh Sun Strike Shield - 75% reduction
+
+ -- Deadmines (Heroic)
+ [47162] = {coef = -60, buff = 88009}, -- Glubtok Frost Shield - 60% reduction
+
+ -- Shadowfang Keep (Heroic)
+ [46962] = {coef = -50, buff = 93468}, -- Baron Ashbury Pain and Suffering - 50% reduction
+
+ -- Zul'Aman (Heroic)
+ [23574] = {coef = -75, buff = 43648}, -- Akil'zon Electrical Storm - 75% reduction
+ [24239] = {coef = -90, buff = 43590}, -- Hex Lord Spirit Bolts Shield - 90% reduction
+
+ -- Zul'Gurub (Heroic)
+ [52258] = {coef = -80, buff = 96776}, -- Gri'lek Pursuit Shield - 80% reduction
+
+ -- End Time
+ [54431] = {coef = -75, buff = 101927}, -- Echo of Baine Pulverize Shield - 75% reduction
+
+ -- Well of Eternity
+ [54853] = {coef = -99, buff = 104600}, -- Peroth'arn Endless Frenzy - 99% reduction
+
+ -- Hour of Twilight
+ [54590] = {coef = -80, buff = 102582}, -- Arcurion Icy Tomb - 80% reduction
+ [54432] = {coef = -90, buff = 103780}, -- Archbishop Benedictus Twilight Shear Shield - 90% reduction
+
+ -- Firelands
+ [52530] = {coef = -99, buff = 99516}, -- Alysrazor Blazing Power - 99% reduction
+
+ --[[===========================]]
+ --[[ MISTS OF PANDARIA CLASSIC ]]
+ --[[===========================]]
+
+ -- Stormstout Brewery
+ [59479] = {coef = -90, buff = 115003}, -- Yan-Zhu Wall of Suds - 90% damage reduction
+
+ -- Scarlet Halls
+ [59303] = {coef = -50, buff = 113395}, -- Armsmaster Harlan Blade Storm - 50% damage reduction
+
+ -- Scholomance
+ [58791] = {coef = -75, buff = 111606}, -- Rattlegore Rusting - 75% damage reduction
+
+ -- Mogu'shan Palace
+ [61398] = {coef = -50, buff = 119946}, -- Gekkan Reckless Inspiration - 50% damage reduction
+
+ -- Siege of Niuzao Temple
+ [62205] = {coef = -70, buff = 119073}, -- Wing Leader Ner'onok Gusting Winds - 70% damage reduction
+
+ -- Throne of Thunder (Raid)
+ [68476] = {coef = -90, buff = 136413}, -- Horridon Dinomancy Shield - 90% damage reduction
+ [69427] = {coef = -95, buff = 137654}, -- Tortos Spinning Shell - 95% damage reduction
+ [68397] = {coef = -99, buff = 134691}, -- Council of Elders Blessed Loa Spirit - 99% damage reduction
+
+ -- Siege of Orgrimmar (Raid)
+ [71479] = {coef = -99, buff = 143480}, -- Malkorok Ancient Barrier - 99% damage reduction
+ [71543] = {coef = -75, buff = 143502}, -- Siegecrafter Blackfuse Protective Frenzy - 75% damage reduction
+ [71152] = {coef = -90, buff = 145071}, -- Paragons Genetic Alteration - 90% damage reduction
+
+ --[[===========================]]
+ --[[ LATER EXPANSIONS (KEEP) ]]
+ --[[===========================]]
+
-- Proving Grounds
[71072] = {coef = -90, buff = 142427}, -- Proving ground Sha shielded (will unshield later so better wait)
[71064] = {coef = -100, buff = 142174, frontal = true}, -- when shielded and we are in front of unit, dont attack
diff --git a/System/Lists/Spells.lua b/System/Lists/Spells.lua
index 1a0889f9a8..bbbf9d53fa 100644
--- a/System/Lists/Spells.lua
+++ b/System/Lists/Spells.lua
@@ -1,5639 +1,68 @@
local _, br = ...
-local function flipRace()
- local race = select(2, br._G.UnitRace("player"))
- local class = select(3, br._G.UnitClass("player"))
- if br.UnitBuffID("player", 193863) then
- if race == "Orc" then
- return "Dwarf"
- elseif race == "Undead" then
- return "Human"
- elseif race == "Troll" then
- if class == 7 then
- return "Draenei"
- elseif class == 9 then
- return "Human"
- else
- return "NightElf"
- end
- elseif race == "Tauren" then
- if class == 11 then
- return "NightElf"
- else
- return "Draenei"
- end
- elseif race == "BloodElf" then
- if class == 12 then
- return "NightElf"
- else
- return "Human"
- end
- elseif race == "Goblin" then
- if class == 3 or class == 7 then
- return "Dwarf"
- else
- return "Gnome"
- end
- elseif race == "Pandaren" then
- return "Pandaren"
- elseif race == "HighmountainTauren" then
- if class == 11 then
- return "NightElf"
- else
- return "Draenei"
- end
- elseif race == "Nightborne" then
- if class == 9 then
- return "Human"
- else
- return "NightElf"
- end
- elseif race == "MagharOrc" then
- return "DarkIronDwarf"
- end
- elseif br.UnitBuffID("player", 193864) then
- if race == "Worgen" then
- return "Troll"
- elseif race == "DarkIronDwarf" then
- return "MagharOrc"
- elseif race == "Human" then
- if class == 2 then
- return "BloodElf"
- else
- return "Undead"
- end
- elseif race == "NightElf" then
- if class == 12 then
- return "BloodElf"
- else
- return "Troll"
- end
- elseif race == "Dwarf" then
- if class == 2 then
- return "Tauren"
- elseif class == 5 then
- return "Undead"
- else
- return "Orc"
- end
- elseif race == "Draenei" then
- if class == 8 then
- return "Orc"
- else
- return "Tauren"
- end
- elseif race == "Pandaren" then
- return "Pandaren"
- elseif race == "Gnome" then
- if class == 10 then
- return "BloodElf"
- else
- return "Goblin"
- end
- elseif race == "VoidElf" then
- if class == 3 or class == 5 or class == 1 then
- return "BloodElf"
- else
- return "Troll"
- end
- elseif race == "LightforgedDraenei" then
- if class == 8 then
- return "Orc"
- else
- return "Tauren"
- end
- end
- end
-end
-function br.getRacial(thisRace)
- local forTheAlliance = br.UnitBuffID("player", 193863) or false
- local forTheHorde = br.UnitBuffID("player", 193864) or false
- local race = select(2, br._G.UnitRace("player"))
- if forTheAlliance or forTheHorde then
- race = flipRace()
- end
- local BloodElfRacial
- local DraeneiRacial
- local OrcRacial
-
- if race == "BloodElf" or race == thisRace then
- BloodElfRacial = select(7, br._G.GetSpellInfo(br._G.GetSpellInfo(69179)))
- end
- if race == "Draenei" or race == thisRace then
- DraeneiRacial = select(7, br._G.GetSpellInfo(br._G.GetSpellInfo(28880)))
- end
- if race == "Orc" or race == thisRace then
- OrcRacial = select(7, br._G.GetSpellInfo(br._G.GetSpellInfo(33702)))
- end
- local racialSpells = {
- -- Alliance
- Dwarf = 20594, -- Stoneform
- Gnome = 20589, -- Escape Artist
- Draenei = DraeneiRacial, -- Gift of the Naaru
- Human = 59752, -- Every Man for Himself
- NightElf = 58984, -- Shadowmeld
- Worgen = 68992, -- Darkflight
- -- Horde
- BloodElf = BloodElfRacial, -- Arcane Torrent
- Goblin = 69041, -- Rocket Barrage
- Orc = OrcRacial, -- Blood Fury
- Tauren = 20549, -- War Stomp
- Troll = 26297, -- Berserking
- Scourge = 7744, -- Will of the Forsaken
- -- Both
- Pandaren = 107079, -- Quaking Palm
- -- Allied Races
- HighmountainTauren = 255654, -- Bull Rush
- LightforgedDraenei = 255647, -- Light's Judgment
- Nightborne = 260364, -- Arcane Pulse
- VoidElf = 256948, -- Spatial Rift
- DarkIronDwarf = 265221, -- Fireblood
- MagharOrc = 274738, -- Ancestral Call
- ZandalariTroll = 291944, -- Regeneratin'
- KulTiran = 287712, -- Haymaker
- Vulpera = 312411, -- Bag of Tricks
- Mechagnome = 312924, -- Hyper Organic Light Originator
- }
- if thisRace ~= nil and racialSpells[thisRace] ~= nil then return racialSpells[thisRace] end
- return racialSpells[race]
- -- return racialSpells[race]
-end
+-- Spell List Loader - loads expansion-specific spell lists
+-- This file determines which spell list to load based on the current WoW expansion
if br.lists == nil then br.lists = {} end
-br.lists.spells = {
- DEATHKNIGHT = {
- -- Blood
- [250] = {
- abilities = {
- deathsCaress = 195292,
- },
- artifacts = {
- allConsumingRot = 192464,
- bloodFeast = 192548,
- bonebreaker = 192538,
- coagulopathy = 192460,
- consumption = 205223,
- danceOfDarkness = 192514,
- grimPerseverance = 192447,
- ironHeart = 192450,
- meatShield = 192453,
- mouthOfHell = 192570,
- rattlingBones = 192557,
- sanguinaryAffinity = 221775,
- skeletalShattering = 192558,
- theHungeringMaw = 214903,
- umbilicusEternus = 193213,
- unendingThirst = 192567,
- veinrender = 192457,
- },
- buffs = {
- boneShield = 195181,
- bloodShield = 77535,
- coagulopathy = 391477,
- crimsonScourge = 81136,
- dancingRuneWeapon = 81256,
- hemostasis = 273947,
- ossuary = 219786,
- runeTap = 194679,
- tombstone = 219809,
- vampiricBlood = 55233,
- vampiricStrength = 408356,
- },
- debuffs = {
- asphyxiate = 221562,
- blooddrinker = 206931,
- heartStrike = 206930,
- markOfBlood = 206940,
- },
- glyphs = {
-
- },
- talents = {
- antimagicBarrier = 205727,
- bloodBoil = 50842,
- bloodFeast = 391386,
- bloodTap = 221699,
- blooddrinker = 206931,
- bloodiedBlade = 458753,
- bloodshot = 391398,
- bloodworms = 195679,
- boneCollector = 458572,
- bonestorm = 194844,
- carnage = 458752,
- coagulopathy = 391477,
- consumption = 274156,
- dancingRuneWeapon = 49028,
- everlastingBond = 377668,
- foulBulwark = 206974,
- gorefiendsGrasp = 108199,
- gripOfTheDead = 273952,
- heartStrike = 206930,
- heartbreaker = 221536,
- heartrend = 377655,
- hemostasis = 273946,
- improvedBoneShield = 374715,
- improvedHeartStrike = 374717,
- improvedVampiricBlood = 317133,
- insatiableBlade = 377637,
- ironHeart = 391395,
- leechingStrike = 377629,
- markOfBlood = 206940,
- marrowrend = 195182,
- ossifiedVitriol = 458744,
- ossuary = 219786,
- perseveranceOfTheEbonBlade = 374747,
- purgatory = 114556,
- rapidDecomposition = 194662,
- redThirst = 205723,
- reinforcedBones = 374737,
- relishInBlood = 317610,
- runeTap = 194679,
- sanguineGround = 391458,
- shatteringBone = 377640,
- tighteningGrasp = 206970,
- tombstone = 219809,
- umbilicusEternus = 391517,
- vampiricBlood = 55233,
- voracious = 273953,
- willOfTheNecropolis = 206967,
- },
- },
- -- Frost
- [251] = {
- abilities = {
- empowerRuneWeapon = 47568,
- frostStrike = 49143,
- frostwyrmsFury = 279302,
- howlingBlast = 49184,
- obliterate = 49020,
- pillarOfFrost = 51271,
- remorselessWinter = 196770,
- },
- artifacts = {
-
- },
- buffs = {
- breathOfSindragosa = 155166,
- coldHeart = 281210,
- darkSuccor = 101568,
- empowerRuneWeapon = 47568,
- eradicatingBlow = 337936,
- frozenPulse = 195750,
- icyCitadel = 272723,
- icyTalons = 194879,
- killingMachine = 51124,
- pillarOfFrost = 51271,
- remorselessWinter = 196770,
- rime = 59052,
- unleashedFrenzy = 338501,
- },
- conduits = {
- eradicatingBlow = 337934,
- everfrost = 337988,
- unleashedFrenzy = 338492,
- },
- debuffs = {
- breathOfSindragosa = 155166,
- remorselessWinter = 196771,
- },
- glyphs = {
-
- },
- talents = {
- asphyxiate = 108194,
- avalanche = 207142,
- breathOfSindragosa = 152279,
- coldHeart = 281208,
- frostscythe = 207230,
- frozenPulse = 194909,
- gatheringStorm = 194912,
- glacialAdvance = 194913,
- hornOfWinter = 57330,
- hypothermicPresence = 321995,
- icecap = 207126,
- inexorableAssault = 253593,
- murderousEffeciency = 207061,
- obliteration = 281238,
- },
- traits = {
- frozenTempest = 278487,
- icyCitadel = 272718,
- }
- },
- -- Unholy
- [252] = {
- abilities = {
- apocalypse = 275699,
- armyOfTheDead = 42650,
- darkTransformation = 63560,
- epidemic = 207317,
- festeringStrike = 85948,
- outbreak = 77575,
- scourgeStrike = 55090,
- -- Pet Abilities
- claw = 47468,
- gnaw = 47481,
- huddle = 47484,
- hook = 212468,
- leap = 47482,
- },
- artifacts = {
-
- },
- buffs = {
- corpseShield = 207319,
- darkSuccor = 101568,
- darkTransformation = 63560,
- festermight = 274373,
- huddle = 91838,
- necrosis = 207346,
- soulReaper = 215711,
- suddenDoom = 49530,
- runicCorruption = 51460,
- unholyAssault = 207289,
- unholyBlight = 115989,
- empowerRuneWeapon = 47568,
- commanderOfTheDead = 390259,
- summonGargoyle = 61777,
- },
- conduits = {
- convocationOfTheDead = 338553,
- },
- debuffs = {
- festeringWound = 194310,
- unholyBlight = 115994,
- virulentPlague = 191587,
- rottenTouch = 390275,
- deathRot = 377537,
-
- },
- glyphs = {
-
- },
- talents = {
- allWillServe = 194916,
- armyOfTheDamned = 276837,
- burstingSores = 207264,
- clawingShadows = 207311,
- defile = 152280,
- ebonFever = 207269,
- gripOfTheDead = 273952,
- harbingerOfDoom = 276023,
- infectedClaws = 207272,
- pestilence = 277234,
- summonGargoyle = 49206,
- unholyAssault = 207289,
- unholyBlight = 115989,
- unholyPact = 319230,
- wraithWalk = 212552,
- commanderOfTheDead = 390259,
- deathRot = 377537,
- improvedDeathCoil = 377580,
- coilOfDevastation = 390270,
- festermight = 377590,
- rottenTouch = 390275,
- vileContagion = 390279,
- },
- traits = {
- magusOfTheDead = 288417,
- },
- },
- -- All
- Shared = {
- abilities = {
-
- antiMagicShell = 48707,
- chainsOfIce = 45524,
- darkCommand = 56222,
- deathAndDecay = 43265,
- deathCoil = 47541,
- deathGate = 50977,
- deathGrip = 49576,
- deathsAdvance = 48265,
- lichborne = 49039,
- pathOfFrost = 3714,
- raiseAlly = 61999,
- runeforging = 53428,
- },
- artifacts = {
-
- },
- buffs = {
- antiMagicShell = 48707,
- coldHeartItem = 235599,
- deathAndDecay = 188290,
- deathsDue = 324164,
- iceboundFortitude = 48792,
- icyTalons = 194878,
- pathOfFrost = 3714,
- unholyStrength = 53365,
- wraithWalk = 212552,
- },
- covenants = {
- abominationLimb = 315443,
- deathsDue = 324128,
- shackleTheUnworthy = 312202,
- swarmingMist = 311648,
- },
- debuffs = {
- bloodPlague = 55078,
- chainsOfIce = 45524,
- controlUndead = 111673,
- darkCommand = 56222,
- frostBreath = 190780,
- frostFever = 55095,
- razorice = 51714,
- soulReaper = 343294,
- },
- glyphs = {
-
- },
- runeforges = {
- bitingCold = 334678,
- deadliestCoil = 334949,
- phearomones = 335177,
- superstrain = 334974,
- },
- talents = {
- runeStrike = 316239,
- abominationLimb = 383269,
- antimagicZone = 51052,
- asphyxiate = 221562,
- assimilation = 374383,
- blindingSleet = 207167,
- bloodDraw = 374598,
- bloodScent = 374030,
- brittle = 374504,
- cleavingStrikes = 316916,
- coldthirst = 378848,
- controlUndead = 111673,
- deathStrike = 49998,
- deathPact = 48743,
- deathsEcho = 356367,
- deathsReach = 276079,
- enfeeble = 392566,
- gloomWard = 391571,
- icePrison = 454786,
- iceboundFortitude = 48792,
- icyTalons = 194878,
- improvedDeathStrike = 374277,
- insidiousChill = 391566,
- marchOfDarkness = 391546,
- mindFreeze = 47528,
- nullMagic = 454842,
- osmosis = 454835,
- permafrost = 207200,
- proliferatingChill = 373930,
- raiseDead = 46585,
- runeMastery = 374574,
- runicAttenuation = 207104,
- runicProtection = 454788,
- sacrificialPact = 327574,
- soulReaper = 343294,
- subduingGrasp = 454822,
- suppression = 374049,
- unholyBond = 374261,
- unholyEndurance = 389682,
- unholyGround = 374265,
- unyieldingWill = 457574,
- vestigialShell = 454851,
- veteranOfTheThirdWar = 48263,
- wraithWalk = 212552,
- },
- talentsHeroic = {
- -- Deathbringer
- bindInDarkness = 440031,
- bloodFever = 440002,
- darkTalons = 436687,
- deathsMessenger = 437122,
- expellingShield = 439948,
- exterminate = 441378,
- grimReaper = 434905,
- pactOfTheDeathbringer = 440476,
- painfulDeath = 443564,
- reapersMark = 439843,
- runeCarvedPlates = 440282,
- soulRupture = 437161,
- swiftEnd = 443560,
- waveOfSouls = 439851,
- witherAway = 441894,
- -- Rider of the Apocalypse
- aFeastOfSouls = 444072,
- apocalypseNow = 444040,
- deathCharge = 444010,
- furyOfTheHorsemen = 444069,
- horsemansAid = 444074,
- hungeringThirst = 444037,
- mawswornMenace = 444099,
- mograinesMight = 444047,
- nazgrimsConquest = 444052,
- onAPalerHorse = 444008,
- pactOfTheApocalypse = 444083,
- ridersChampion = 444005,
- trollbanesIcyFury = 444097,
- whitemanesFamine = 444033,
- -- Sanlayn
- bloodsoakedGround = 434033,
- bloodyFortitude = 434136,
- frenziedBloodthirst = 434075,
- giftOfTheSanlayn = 434152,
- inciteTerror = 434151,
- inflictionOfSorrow = 434143,
- newlyTurned = 433934,
- pactOfTheSanlayn = 434261,
- sanguineScent = 434263,
- theBloodIsLife = 434260,
- vampiricAura = 434100,
- vampiricSpeed = 434028,
- vampiricStrike = 433901,
- visceralStrength = 434157,
- }
- },
- },
- DEMONHUNTER = {
- -- Havoc
- [577] = {
- abilities = {
- annihilation = 201427,
- bladeDance = 188499,
- blur = 198589,
- chaosStrike = 162794,
- deathSweep = 210152,
- demonsBite = 162243,
- felRush = 195072,
- },
- artifacts = {
-
- },
- buffs = {
- furiousGaze = 343312,
- initiative = 391215,
- innerDemon = 337313,
- metamorphosis = 162264,
- momentum = 208628,
- potion = 371024,
- prepared = 203650,
- unboundChaos = 347462, --234059,
- },
- conduits = {
- serratedGlaive = 339230,
- },
- debuffs = {
- essenceBreak = 320338,
- exposedWound = 339229,
- },
- glyphs = {
-
- },
- items = {
- algetharPuzzleBox = 193701,
- augmentation = 201325,
- beaconToTheBeyond = 203963,
- dragonfireBombDispenser = 202610,
- elementiumPocketAnvil = 202617,
- irideusFragment = 193743,
- manicGrieftorch = 194308,
- potion = 191381,
- stormeatersBoon = 194302,
- },
- talents = {
- aFireInside = 427775,
- acceleratedBlade = 391275,
- anyMeansNecessary = 388114,
- blindFury = 203550,
- burningHatred = 320374,
- burningWound = 391189,
- chaosTheory = 389687,
- chaoticDisposition = 428492,
- chaoticTransformation = 388112,
- criticalChaos = 320413,
- cycleOfHatred = 258887,
- dancingWithFate = 389978,
- dashOfChaos = 427794,
- deflectingDance = 427776,
- demonBlades = 203555,
- demonHide = 428241,
- desperateInstincts = 205411,
- essenceBreak = 258860,
- eyeBeam = 198013,
- felBarrage = 258925,
- felblade = 232893,
- firstBlood = 206416,
- furiousGaze = 343311,
- furiousThrows = 393029,
- glaiveTempest = 342817,
- growingInferno = 390158,
- improvedChaosStrike = 343206,
- improvedFelRush = 343017,
- inertia = 427640,
- initiative = 388108,
- innerDemon = 389693,
- insatiableHunger = 258876,
- isolatedPrey = 388113,
- knowYourEnemy = 388118,
- looksCanKill = 320415,
- momentum = 206476,
- mortalDance = 328725,
- netherwalk = 196555,
- ragefire = 388107,
- relentlessOnslaught = 389977,
- restlessHunter = 390142,
- scarsOfSuffering = 428232,
- serratedGlaive = 390154,
- shatteredDestiny = 388116,
- soulscar = 388106,
- tacticalRetreat = 389688,
- trailOfRuin = 258881,
- unboundChaos = 347461,
- },
- traits = {
- chaoticTransformation = 288754,
- eyesOfRage = 278500,
- furiousGaze = 273231,
- revolvingBlades = 279581,
- unboundChaos = 275144,
- },
- },
- -- Vengeance
- [581] = {
- abilities = {
- demonSpikes = 203720,
- --felDevastation = 212084,
- --fieryBrand = 204021,
- immolationAura = 258920,
- infernalStrike = 189110,
- metamorphosis = 187827,
- shear = 203782,
- --sigilOfChains = 202138,
- --sigilOfMisery = 207684,
- soulCleave = 228477,
- throwGlaive = 204157,
- },
- artifacts = {
-
- },
- buffs = {
- bladeTurning = 247254,
- demonSpikes = 203819,
- empowerWards = 218256,
- feastofSouls = 207693,
- felBombardment = 337775,
- metamorphosis = 187827,
- soulFragments = 203981,
- siphonedPower = 218561,
- },
- debuffs = {
- fieryBrand = 207744,
- frailty = 247456,
- },
- glyphs = {
-
- },
- items = {
- augmentation = 201325,
- potion = 191383,
- },
- talents = {
- agonizingFlames = 207548,
- ascendingFlame = 428603,
- auraOfPain = 207347,
- bulkExtraction = 320341,
- burningAlive = 207739,
- burningBlood = 390213,
- calcifiedSpikes = 389720,
- chainsOfAnger = 389715,
- charredFlesh = 336639,
- charredWarblades = 213010,
- cycleOfBinding = 389718,
- darkglareBoon = 389708,
- deflectingSpikes = 321028,
- downInFlames = 389732,
- extendedSpikes = 389721,
- fallout = 227174,
- feastofSouls = 207697,
- feedTheDemon = 218612,
- felDevastation = 212084,
- felFlameFortification = 389705,
- felblade = 232893,
- fieryBrand = 204021,
- fieryDemise = 389220,
- focusedCleave = 343207,
- fracture = 263642,
- frailty = 389958,
- illuminatedSigils = 428557,
- infernalArmor = 320331,
- lastResort = 209258,
- meteoricStrikes = 389724,
- painbringer = 207387,
- perfectlyBalancedGlaive = 320387,
- quickenedSigils = 209281,
- retaliation = 389729,
- revelInPain = 343014,
- roaringFire = 391178,
- ruinousBulwark = 326853,
- shearFury = 389997,
- sigilOfChains = 202138,
- sigilOfSilence = 202137,
- soulBarrier = 263648,
- soulCarver = 207407,
- soulFurnace = 391165,
- soulCrush = 389985,
- soulmonger = 389711,
- spiritBomb = 247454,
- stokeTheFlames = 393827,
- voidReaver = 268175,
- volatileFlameblood = 390808,
- vulnerability = 389976,
- },
- },
- -- Initial Demon Hunter 8-10
- [1456] = {
- abilities = {
- chaosStrike = 344862, --162794,
- demonsBite = 344859, --162243,
- felRush = 344865, --195072,
- sigilOfFlame = 204596,
- },
- buffs = {
-
- },
- debuffs = {
- sigilOfFlame = 204598,
- },
- },
- -- All
- Shared = {
- abilities = {
- chaosStrike = 344862,
- demonsBite = 344859,
- disrupt = 183752,
- felRush = 344865,
- glide = 131347,
- immolationAura = 258920,
- metamorphosis = 191427,
- sigilOfFlame = 204596,
- soulCarver = 214743,
- spectralSight = 188501,
- throwGlaive = 185123,
- torment = 185245,
- },
- artifacts = {
-
- },
- buffs = {
- chaosTheory = 337567,
- felBarrage = 258925,
- felBombardment = 337849,
- felCrystalInfusion = 193547,
- gazeOfTheLegion = 193456,
- glide = 131347,
- immolationAura = 258920,
- inertia = 427641,
- tacticalRetreat = 389890,
- twistedHeart = 396046,
- },
- covenants = {
- -- elysianDecree = 306830,
- -- fodderToTheFlame = 329554,
- -- sinfulBrand = 317009,
- -- theHunt = 323639,
- },
- debuffs = {
- burningWound = 346278,
- essenceBreak = 320338,
- sigilOfFlame = 204598,
- sinfulBrand = 317009,
- },
- glyphs = {
- glyphOfCracklingFlames = 219831,
- glyphOfFallowWings = 220083,
- glyphOfFearsomeMetamorphosis = 220831,
- glyphOfFelTouchedSouls = 219713,
- glyphOfFelWings = 220228,
- glyphOfFelEnemies = 220240,
- glyphOfManaTouchedSouls = 219744,
- glyphOfShadowEnemies = 220244,
- glyphOfTatteredWings = 220226,
- },
- runeforges = {
- agonyGaze = 355886,
- burningWound = 346279,
- chaosTheory = 337551,
- darkglareMedallion = 337534,
- felBombardment = 337775,
- razelikhsDefilement = 337544,
- },
- talents = {
- aldrachiDesign = 391409,
- blazingPath = 320416,
- bouncingGlaives = 320386,
- championOfTheGlaive = 429211,
- chaosFragments = 320412,
- chaosNova = 179057,
- collectiveAnguish = 390152,
- consumeMagic = 278326,
- darkness = 196718,
- demonic = 213410,
- demonMuzzle = 388111,
- elysianDecree = 390163,
- erraticFelheart = 391397,
- felfireHaste = 389846,
- flamesOfFury = 389694,
- illidariKnowledge = 389696,
- imprison = 217832,
- improvedDisrupt = 320361,
- improvedSigilOfMisery = 320418,
- internalStruggle = 393822,
- liveByTheGlaive = 428607,
- longNight = 389781,
- lostInDarkness = 389849,
- masterOfTheGlaive = 389763,
- pitchBlack = 389783,
- preciseSigils = 389799,
- pursuit = 320654,
- rushOfChaos = 320421,
- shatteredRestoration = 389824,
- sigilOfMisery = 207684,
- soulRending = 204909,
- soulSigils = 395446,
- swallowedAnger = 320313,
- theHunt = 370965,
- unrestrainedFury = 320770,
- vengefulBonds = 320635,
- vengefulRetreat = 198793,
- willOfTheIllidari = 389695,
- },
- },
- },
- DRUID = {
- -- Initial
- [1447] = {
-
- },
- -- Balance
- [102] = {
- abilities = {
- celestialAlignment = 194223,
- fullMoon = 274283,
- furyOfElune = 202770,
- halfMoon = 274282,
- innervate = 29166,
- starfire = 194153,
- newMoon = 274281,
- removeCorruption = 2782,
- solarBeam = 78675,
- typhoon = 132469,
- wrath = 190984,
- starfall = 191034,
- starsurge = 78674,
- sunfire = 93402,
- stellarFlare = 202347,
- },
- artifacts = {
-
- },
- buffs = {
- blessingOfElune = 202737,
- blessingOfAnshe = 202739,
- incarnationChoseOfElune = 102560,
- celestialAlignment = 194223,
- eclipse_lunar = 48518,
- eclipse_solar = 48517,
- onethsOverconfidence = 209407,
- onethsIntuition = 209406,
- solarEmpowerment = 164545,
- innervate = 29166,
- lunarEmpowerment = 164547,
- warriorOfElune = 202425,
- balanceForm = 24858,
- stellarDrift = 202461,
- emeraldDreamcatcher = 208190,
- owlkinFrenzy = 157228,
- powerOfEluneTheMoonGoddness = 208284,
- sephuzSecret = 208052,
- astralAcceleration = 242232,
- newMoonController = -2027671,
- solstice = 343648,
- starfall = 191034,
- starLord = 279709, --backwards compatible
- starlord = 279709,
- livelySpirit = 279279,
- arcanicPulsar = 287790,
- primordialArcanicPulsar = 338668,
- balanceOfAllThings = 339942,
- balanceOfAllThingsNature = 339943, -- Nature Buff
- balanceOfAllThingsArcane = 339946, -- Arcane Buff
- timewornDreambinder = 339949,
- kindredEmpowerment = 327022,
- onethsClearVision = 338661,
- ravenousFrenzy = 323546,
-
- },
- conduits = {
- preciseAlignment = 340706,
-
-
- },
- covenants = {
- loneEmpowerment = 338142,
- ravenousFrenzy = 323546,
- empowerBond = 326446,
- },
- debuffs = {
- stellarFlare = 202347,
- moonfire = 164812,
- sunfire = 164815,
- stellarEmpowerment = 197637,
- },
- glyphs = {
- },
- runeforges = {
- balanceOfAllThings = 339942,
- lycarasFleetingGlimpse = 340059,
- primordialArcanicPulsar = 338668,
- timewornDreambinder = 339949,
-
- },
- talents = {
- aetherialKindling = 327541,
- astralCommunion = 202359,
- astralSmolder = 394058,
- balanceOfAllThings = 394049,
- bloomingInfusion = 429433,
- bounteousBloom = 429215,
- celestialAlignment = 194223,
- cenariusMight = 455797,
- controlOfTheDream = 434249,
- convokeTheSpirits = 323764,
- denizenOfTheDream = 394076,
- dreamSurge = 433831,
- durabilityOfNature = 429227,
- earlySpring = 428937,
- eclipse = 79577,
- elunesGuidance = 393991,
- expansiveness = 429399,
- forceOfNature = 205636,
- furyOfElune = 202770,
- grovesInspiration = 429402,
- harmonyOfTheGrove = 428731,
- incarnationChosenOfElune = 394013,
- lightOfTheSun = 202918,
- lunarShrapnel = 393868,
- naturesBalance = 202430,
- naturesGrace = 393959,
- newMoon = 274281,
- orbitBreaker = 383197,
- potentEnchantments = 429420,
- powerOfGoldrinn = 394046,
- powerOfNature = 428859,
- powerOfTheDream = 434220,
- protectiveGrowth = 433748,
- radiantMoonlight = 394121,
- rattleTheStars = 393954,
- shootingStars = 202342,
- solarBeam = 78675,
- solstice = 343647,
- soulOfTheForest = 114107,
- starfall = 191034,
- starlord = 202345,
- starweaver = 393940,
- stellarFlare = 202347,
- sunderedFirmament = 294108,
- treantsOfTheMoon = 428544,
- twinMoons = 279620,
- umbralEmbrace = 393760,
- umbralIntensity = 383195,
- waningTwilight = 393956,
- warriorOfElune = 202425,
- wildMushroom = 88747,
- },
- traits = {
- arcanicPulsar = 287773,
- dawningSun = 276152,
- highNoon = 278505,
- lunarShrapnel = 278507,
- powerOfTheMoon = 273367,
- streakingStars = 272871,
- sunblaze = 274397,
- }
- },
- -- Feral
- [103] = {
- abilities = {
- adaptiveSwarmDamage = 391889, -- Not Castable but needed for In-Flight
- adaptiveSwarmHeal = 391891, -- Not Castable but needed for In-Flight
- moonfireCat = 155625,
- },
- buffs = {
- adaptiveSwarmDamage = 391889,
- adaptiveSwarmHeal = 391891,
- apexPredator = 255984,
- apexPredatorsCraving = 391882, --339140,
- berserk = 106951,
- bloodtalons = 145152,
- clearcasting = 135700,
- elunesGuidance = 202060,
- fieryRedMaimers = 236757,
- incarnationAvatarOfAshamane = 102543,
- ironJaws = 276021,
- jungleStalker = 252071,
- overflowingPower = 405189,
- predatorRevealed = 408468,
- predatorySwiftness = 69369,
- ravage = 441585,
- sabertooth = 391722,
- savageRoar = 52610,
- scentOfBlood = 285646,
- smolderingFrenzy = 422751,
- stampedingRoar = 77764,
- suddenAmbush = 391974,
- survivalInstincts = 61336,
- tigersFury = 5217,
- },
- debuffs = {
- adaptiveSwarmDamage = 391889,
- adaptiveSwarmHeal = 391891,
- bloodseekerVines = 439531,
- direFixation = 417713,
- feralFrenzy = 274838,
- moonfireCat = 155625,
- primalWrath = 1079,
- rakeStun = 163505,
- thrashCat = 405233,
- },
- items = {
- algetharPuzzleBox = 193701,
- ashesOfTheEmbersoul = 207167,
- augmentation = 201325, -- Generic, place item id of desired augmentation
- bandolierOfTwistedBlades = 207165,
- fyrakksTaintedRageheart = 207164,
- manicGrieftorch = 194308,
- mirrorOfFracturedTomorrows = 207581,
- mydasTalisman = 158319,
- potion = 191383, -- Generic, place item id of desired potion
- witherbarksBranch = 109999,
- },
- talents = {
- adaptiveSwarm = 391888,
- aggravateWounds = 441829,
- apexPredatorsCraving = 391881,
- ashamanesGuidance = 391548,
- berserk = 106951,
- berserkFrenzy = 384668,
- berserkHeartOfTheLion = 391174,
- bestialStrength = 441841,
- bloodtalons = 319439,
- bondWithNature = 439929,
- brutalSlash = 202028,
- carnivorousInstinct = 390902,
- circleOfLifeAndDeath = 400320,
- clawRampage = 441835,
- coiledToSpring = 449537,
- convokeTheSpirits = 391528,
- doubleclawedRake = 391700,
- dreadfulBleeding = 391045,
- dreadfulWound = 441809,
- empoweredShapeshifting = 441689,
- entanglingVortex = 439895,
- feralFrenzy = 274837,
- flowerWalk = 439901,
- fountOfStrength = 441675,
- franticMomentum = 391875,
- harmoniousConstitution = 440116,
- huntBeneathTheOpenSkies = 439868,
- implant = 440118,
- incarnationAvatarOfAshamane = 102543,
- infectedWounds = 48484,
- killingStrikes = 441824,
- lethalPreservation = 455461,
- lionsStrength = 391972,
- lunarInspiration = 155580,
- mercilessClaws = 231063,
- momentOfClarity = 236068,
- omenOfClarity = 16864,
- packsEndurance = 441844,
- pouncingStrikes = 390772,
- predator = 202021,
- predatorySwiftness = 16974,
- primalWrath = 285381,
- ragingFury = 391078,
- rampantFerocity = 391709,
- ravage = 441583,
- ripAndTear = 391347,
- ruthlessAggression = 441814,
- saberJaws = 421432,
- sabertooth = 202031,
- savageFury = 449645,
- soulOfTheForest = 158476,
- strategicInfusion = 439890,
- strikeForTheHeart = 441845,
- suddenAmbush = 384667,
- survivalInstincts = 61336,
- tasteForBlood = 384665,
- tearDownTheMighty = 441846,
- thrashingClaws = 405300,
- thrivingGrowth = 439528,
- tigersFury = 5217,
- tigersTenacity = 391872,
- tirelessEnergy = 383352,
- twinSprouts = 440117,
- unbridledSwarm = 391951,
- veinripper = 391978,
- vigorousCreepers = 440119,
- wildSlashes = 390864,
- wildpowerSurge = 441691,
- wildshapeMastery = 441678,
- wildstalkersPower = 439926,
- },
- },
- -- Guardian
- [104] = {
- abilities = {
- berserk = 50334,
- incapacitatingRoar = 99,
- incarnationGuardianOfUrsoc = 102558,
- maul = 6807,
- removeCorruption = 2782,
- skullBash = 106839,
- stampedingRoar = 106898,
- survivalInstincts = 61336,
- },
- artifacts = {
-
- },
- buffs = {
- berserk = 50334,
- galacticGuardian = 213708,
- goryFur = 201671,
- incarnationGuardianOfUrsoc = 102558,
- gore = 93622,
- pulverize = 158792,
- savageCombatant = 340613,
- survivalInstincts = 61336,
- toothAndClaw = 135286,
- },
- conduits = {
- savageCombatant = 340609,
-
- },
- covenants = {
- loneProtection = 338018,
- },
- debuffs = {
- moonfireGuardian = 164812,
- toothAndClaw = 135601,
- },
- glyphs = {
-
- },
- talents = {
- afterTheWildfire = 371905,
- aggravateWounds = 441829,
- berserkPersistence = 377779,
- berserkRavage = 343240,
- berserkUncheckedAggression = 377623,
- bestialStrength = 441841,
- bloodFrenzy = 203962,
- brambles = 203953,
- bristlingFur = 155835,
- circleOfLifeAndDeath = 391969,
- clawRampage = 441835,
- convokeTheSpirits = 323764,
- dreadfulWound = 441809,
- dreamOfCenarius = 372119,
- earthwarden = 203974,
- elunesFavored = 370586,
- empoweredShapeshifting = 441689,
- flashingClaws = 393427,
- fountOfStrength = 441675,
- -- frontOfThePack = 377835,
- furyOfNature = 370695,
- galacticGuardian = 203964,
- gore = 210706,
- goryFur = 200854,
- guardianOfElune = 213680,
- improvedSurvivalInstincts = 328767,
- incarnationGuardianOfUrsoc = 102558,
- infectedWounds = 345208,
- innateResolve = 377811,
- killingStrikes = 441824,
- layeredMane = 279552,
- mangle = 231064,
- maul = 6807,
- packsEndurance = 441844,
- pulverize = 80313,
- rageOfTheSleeper = 200851,
- ravage = 441583,
- reinforcedFur = 393618,
- reinvigoration = 372945,
- rendAndTear = 204053,
- ruthlessAggression = 441814,
- scintillatingMoonlight = 238049,
- soulOfTheForest = 158477,
- strikeForTheHeart = 441845,
- survivalInstincts = 61336,
- survivalOfTheFittest = 203965,
- tearDownTheMighty = 441846,
- toothAndClaw = 135288,
- twinMoonfire = 372567,
- untamedSavagery = 372943,
- -- ursineAdept = 300346,
- ursocsEndurance = 393611,
- ursocsFury = 372505,
- ursocsGuidance = 393414,
- viciousCycle = 371999,
- vulnerableFlesh = 372618,
- wildpowerSurge = 441691,
- wildshapeMastery = 441678,
- },
- traits = {
- layeredMane = 279552,
- twistedClaws = 275906,
- },
- },
- -- Restoration
- [105] = {
- abilities = {
- efflorescence = 145205,
- innervate = 29166,
- ironbark = 102342,
- lifebloom = 33763,
- naturesCure = 88423,
- nourish = 50464,
- revitalize = 212040,
- wrath = 5176,
- sunfire = 93402,
- tranquility = 740,
- typhoon = 132469,
- yserasGift = 145108,
- naturesSwiftness = 132158,
- },
- artifacts = {
-
- },
- buffs = {
- abundance = 207640,
- cenarionWard = 102352,
- clearcasting = 16870,
- incarnationTreeOfLife = 33891,
- innervate = 29166,
- groveTending = 279793,
- rejuvenationGermination = 155777,
- lifebloom = 33763,
- eclipse_lunar = 48518,
- eclipse_solar = 48517,
- --lunarEmpowerment = 164547,
- regrowth = 8936,
- --solarEmpowerment = 164545,
- soulOfTheForest = 114108,
- cultivat = 200389,
- tranquility = 157982,
- springblossom = 207386,
- symbolOfHope = 64901,
- fullbloom = 290213,
- heartOfTheWild = 108291,
- },
- conduits = {
- loneMeditation = 338035,
- },
- debuffs = {
- moonfire = 164812,
- sunfire = 164815,
- },
- glyphs = {
-
- },
- runeforges = {
- theDarkTitansLesson = 338831,
- verdantInfusion = 338829,
-
- },
- talents = {
- abundance = 207383,
- balanceAffinity = 197632,
- bloomingInfusion = 429433,
- bondWithNature = 439929,
- bounteousBloom = 429215,
- buddingLeaves = 392167,
- burstingGrowth = 440120,
- callOfTheElderDruid = 426784,
- cenarionWard = 102351,
- cenariusGuidance = 393371,
- cenariusMight = 455797,
- controlOfTheDream = 434249,
- cultivation = 200390,
- dreamSurge = 433831,
- durabilityOfNature = 429227,
- earlySpring = 428937,
- entanglingVortex = 439895,
- expansiveness = 429399,
- feralAffinity = 197490,
- flourish = 197721,
- flowerWalk = 439901,
- germination = 155675,
- grovesInspiration = 429402,
- guardianAffinity = 197491,
- harmoniousConstitution = 440116,
- harmonyOfTheGrove = 428731,
- huntBeneathTheOpenSkies = 439868,
- implant = 440118,
- incarnationTreeOfLife = 33891,
- innerPeace = 197073,
- lethalPreservation = 455461,
- nourish = 50464,
- overgrowth = 203651,
- photosynthesis = 274902,
- potentEnchantments = 429420,
- powerOfNature = 428859,
- powerOfTheDream = 434220,
- protectiveGrowth = 433748,
- resilientFlourishing = 439880,
- rootNetwork = 439882,
- soulOfTheForest = 158478,
- springBlossoms = 207385,
- strategicInfusion = 439890,
- thrivingGrowth = 439528,
- treantsOfTheMoon = 428544,
- twinSprouts = 440117,
- vigorousCreepers = 440119,
- wildstalkersPower = 439926,
- },
- traits = {
- dawningSun = 276152,
- highNoon = 278505,
- }
- },
- -- All
- Shared = {
- abilities = {
- barkskin = 22812,
- bearForm = 5487,
- catForm = 768,
- charmWoodlandCreature = 127757, -- Tome of the Wilds
- cyclone = 33786,
- dash = 1850,
- dreamwalk = 193753,
- entanglingRoots = 339,
- ferociousBite = 22568,
- flap = 164862,
- flightForm = 165962,
- growl = 6795,
- mangle = 33917,
- markOfTheWild = 1126,
- moonfire = 8921,
- mountForm = 210053,
- prowl = 5215,
- rebirth = 20484,
- regrowth = 8936,
- revive = 50769,
- shred = 5221,
- sunfire = 164815,
- swipe = 213764,
- swipeBear = 213771,
- swipeCat = 106785,
- teleportMoonglade = 18960,
- thrashBear = 77758,
- thrashCat = 106830,
- trackBeasts = 210065,
- trackHumanoids = 5225,
- travelForm = 783,
- wrath = 5176,
- },
- animas = {
- lycarasBargin = 329960,
- lycarasTwig = 330666,
- },
- artifacts = {
-
- },
- buffs = {
- adaptiveSwarm = 325748,
- barkskin = 22812,
- bearForm = 5487,
- burningEssence = 138927,
- catForm = 768,
- convokeTheSpirits = 391528,
- dash = 1850,
- tigerDash = 252216,
- flightForm = 165962,
- frenziedRegeneration = 22842,
- heartOfTheWild = 319454,
- ironfur = 192081,
- kindredEmpowerment = 327139,
- kindredSpiritsBuff = 326967, --This is buff on ourselves...at least as dps
- kindredEmpowermentEnergize = 327022, --/kindred-empowerment this is when someone else cast it on you
- loneSpirit = 338041,
- lycarasTwig = 330668, -- Anima Power Torghast
- markOfTheWild = 1126,
- moonkinForm = 197625,
- onethsPerception = 339800,
- prowl = 5215,
- rejuvenation = 774,
- regrowth = 8936,
- shadowmeld = 58984,
- soulshape = 310143,
- stagForm = 210053,
- stampedingRoar = 106898,
- stampedingRoarCat = 77764,
- suddenAmbush = 340698,
- travelForm = 783,
- treantForm = 114282,
- wildGrowth = 48438,
- },
- conduits = {
- deepAllegiance = 341378,
- tasteForBlood = 340682,
- },
- covenants = {
- adaptiveSwarm = 325727,
- adaptiveSwarmHeal = 325748,
- convokeTheSpiritsCovenant = 323764,
- kindredSpirits = 326434,
- ravenousFrenzy = 323546,
- },
- debuffs = {
- adaptiveSwarm = 325733,
- cyclone = 33786,
- entanglingRoots = 339,
- growl = 6795,
- hibernate = 2637,
- lycarasBargin = 329961,
- moonfire = 164812, --8921,
- rake = 155722,
- rip = 1079,
- thrashBear = 192090,
- thrashCat = 106830,
- massEntanglement = 102359,
- },
- glyphs = {
- glyphOfTheCheetah = 131113,
- glyphOfTheDoe = 224122,
- glyphOfTheFeralChameleon = 210333,
- glyphOfTheOrca = 114333,
- glyphOfTheSentinel = 219062,
- glyphOfTheUrsolChameleon = 107059,
- },
- runeforges = {
- apexPredatorsCraving = 339139,
- eyeOfFearfullSymmetry = 339141,
- frenzyband = 340053,
- },
- talents = {
- aessinasRenewal = 474678,
- astralInfluence = 197524,
- circleOfTheHeavens = 474541,
- circleOfTheWild = 474530,
- cyclone = 33786,
- felineSwiftness = 131768,
- fluidForm = 449193,
- forestwalk = 400129,
- frenziedRegeneration = 22842,
- galeWinds = 400142,
- grievousWounds = 474526,
- heartOfTheWild = 319454,
- hibernate = 2637,
- improvedBarkskin = 327993,
- improvedRejuvenation = 231040,
- improvedStampedingRoar = 288826,
- incapacitatingRoar = 99,
- incessantTempest = 400140,
- innervate = 29166,
- instinctsOfTheClaw = 449184,
- ironfur = 192081,
- killerInstinct = 108299,
- lingeringHealing = 231040,
- loreOfTheGrove = 449185,
- lycarasInspiration = 1232897,
- lycarasTeachings = 378988,
- maim = 22570,
- massEntanglement = 102359,
- mattedFur = 385786,
- moonkinForm = 24858,
- mightyBash = 5211,
- naturalRecovery = 377796,
- naturesVigil = 124974,
- nurturingInstinct = 33873,
- oakskin = 449191,
- perfectlyhonedInstincts = 1213597,
- primalFury = 159286,
- rake = 1822,
- rejuvenation = 774,
- removeCorruption = 2782,
- renewal = 108238,
- rip = 1079,
- skullBash = 106839,
- soothe = 2908,
- stampedingRoar = 106898,
- starfire = 197628,
- starlightConduit = 451211,
- starsurge = 197626,
- sunfire = 93402,
- symbioticRelationship = 474750,
- thickHide = 16931,
- thrash = 106832,
- tigerDash = 252216,
- typhoon = 132469,
- ursineVigor = 377842,
- ursocsSpirit = 449182,
- ursolsVortex = 102793,
- verdantHeart = 301768,
- wellhonedInstincts = 377847,
- wildCharge = 102401,
- wildGrowth = 48438,
- },
- talentsHeroic = {
- bloomingInfusion = 429433,
- bounteousBloom = 429215,
- burstingGrowth = 440120,
- cenariusMight = 455797,
- controlOfTheDream = 434249,
- dreamSurge = 433831,
- durabilityOfNature = 429227,
- earlySpring = 428937,
- expansiveness = 429399,
- grovesInspiration = 429402,
- harmonyOfTheGrove = 428731,
- potentEnchantments = 429420,
- powerOfNature = 428859,
- powerOfTheDream = 434220,
- protectiveGrowth = 433748,
- resilientFlourishing = 439880,
- rootNetwork = 439882,
- treantsOfTheMoon = 428544,
- },
- traits = {
- livelySpirit = 279642,
- },
- },
- },
- EVOKER = {
- -- Initial Evoker
- [1465] = {
- abilities = {
- fireBreath = 357208,
- },
- },
- -- Devastation
- [1467] = {
- abilities = {
-
- },
- artifacts = {
-
- },
- buffs = {
- burnout = 375802,
- chargedBlast = 370454,
- dragonrage = 375087,
- emeraldTrance = 424155,
- essenceBurst = 359618,
- iridescenceBlue = 386399,
- iridescenceRed = 386353,
- leapingFlames = 370901,
- powerSwell = 376850,
- snapFire = 370818,
- },
- conduits = {
-
- },
- covenants = {
-
- },
- debuffs = {
- shatteringStar = 370452,
- },
- glyphs = {
-
- },
- runeforges = {
-
- },
- talents = {
- animosity = 375797,
- arcaneIntensity = 375618,
- arcaneVigor = 386342,
- azureEssenceBurst = 375721,
- burnout = 375801,
- catalyze = 386283,
- causality = 375777,
- chargedBlast = 370455,
- denseEnergy = 370962,
- dragonrage = 375087,
- engulfingBlaze = 370837,
- eternitySurge = 359073,
- eternitysSpan = 375757,
- eventHorizon = 411164,
- everburningFlame = 370819,
- eyeOfInfinity = 411165,
- feedTheFlames = 369846,
- firestorm = 368847,
- focusingIris = 386336,
- fontOfMagic = 411212,
- heatWave = 375725,
- hoardedPower = 375796,
- honedAggression = 371038,
- immenentDestruction = 370781,
- imposingPresence = 371016,
- innerRadiance = 386405,
- iridescence = 370867,
- layWaste = 371034,
- onyxLegacy = 386348,
- powerSwell = 370839,
- pyre = 357211,
- ragingInferno = 405659,
- rubyEmbers = 365937,
- rubyEssenceBurst = 376872,
- scintillation = 370821,
- shatteringStar = 370452,
- snapFire = 370783,
- spellweaversDominance = 370845,
- titanicWrath = 386272,
- tyranny = 376888,
- volatility = 369089,
- },
- traits = {
-
- },
- },
- -- Preservation
- [1468] = {
- abilities = {
- echo = 364343,
- naturalize = 360823,
- reversion = 366155,
- rewind = 363534,
-
- },
- artifacts = {
-
- },
- buffs = {
- echo = 364343,
- essenceBurst = 369297,
- reversion = 366155,
- timeDilation = 357170,
-
- },
- conduits = {
-
- },
- covenants = {
-
- },
- debuffs = {
-
- },
- glyphs = {
-
- },
- runeforges = {
-
- },
- talents = {
- dreamBreath = 355936,
- emeraldCommunion = 370960,
- spiritbloom = 367226,
- timeDilation = 357170,
- temporalAnomaly = 373861,
-
- },
- traits = {
-
- },
- },
- -- All
- Shared = {
- abilities = {
- azureStrike = 362969,
- blessingOfTheBronze = 364342,
- choosenIdentity = 360022,
- deepBreath = 357210,
- disintegrate = 356995,
- emeraldBlossom = 355913,
- fireBreath = 357208,
- furyOfTheAspects = 390386,
- hover = 358267,
- livingFlame = 361469,
- -- quell = 351338,
- -- renewingBlaze = 374348,
- returnEvoker = 361227,
- tailSwipe = 368970,
- wingBuffet = 357214,
- visage = 351239,
- },
- animas = {
-
- },
- artifacts = {
-
- },
- buffs = {
- ancientFlame = 375583,
- blazingShards = 409848,
- blessingOfTheBronze = 364342,
- hover = 358267,
- scarletAdaptation = 372470,
- soar = 381322,
- sourceOfMagic = 372581,
- tipTheScales = 370553,
- visage = 372014,
- visageForm = 351239,
- },
- conduits = {
-
- },
- covenants = {
-
- },
- debuffs = {
- fireBreath = 357209,
- shatteringStar = 370452,
- },
- glyphs = {
-
- },
- runeforges = {
-
- },
- talents = {
- aerialMastery = 365933,
- ancientFlame = 369990,
- attunedToTheDream = 376930,
- blastFurnace = 375510,
- bountifulBloom = 370886,
- cauterizingFlame = 374251,
- clobberingSweep = 375443,
- draconicLegacy = 376166,
- enkindled = 375554,
- essenceAttunement = 375722,
- expunge = 365585,
- extendedFlight = 375517,
- exuberance = 375542,
- fireWithin = 375577,
- fociOfLife = 375574,
- forgerOfMountains = 375528,
- heavyWingbeats = 368838,
- inherentResistance = 375544,
- innateMagic = 375520,
- instinctiveArcana = 376164,
- landslide = 358385,
- leapingFlames = 369939,
- lushGrowth = 375561,
- naturalConvergence = 369913,
- obsidianBulwark = 375406,
- obsidianScales = 363916,
- oppressingRoar = 372048,
- overawe = 374346,
- panacea = 387761,
- permeatingChill = 370897,
- potentMana = 418101,
- powerNexus = 369908,
- protractedTalons = 369909,
- quell = 351338,
- recall = 371806,
- regenerativeMagic = 387787,
- renewingBlaze = 374348,
- rescue = 370665,
- scarletAdaptation = 372469,
- sleepWalk = 360806,
- sourceOfMagic = 369459,
- tailwind = 375556,
- terrorOfTheSkies = 371032,
- timeSpiral = 374968,
- tipTheScales = 370553,
- twinGuardian = 370888,
- unravel = 368432,
- verdantEmbrace = 360995,
- wallopingBlow = 387341,
- zephyr = 374227,
- },
- traits = {
-
- },
- },
- },
- HUNTER = {
- -- BeastMastery
- [253] = {
- abilities = {
- aspectOfTheWild = 193530,
- barbedShot = 217200,
- bestialWrath = 19574,
- cobraShot = 193455,
- concussiveShot = 5116,
- counterShot = 147362,
- intimidation = 19577,
- killCommand = 34026,
- killShot = 53351,
- multishot = 2643,
- },
- animas = {
- piercingScope = 331183,
- },
- artifacts = {
-
- },
- buffs = {
- aspectOfTheWild = 193530,
- barbedShot = 217200,
- beastCleave = 118455,
- berserking = 26297,
- bestialWrath = 19574,
- danceOfDeath = 274443,
- direBeast = 120694,
- frenzy = 272790,
- parselsTongue = 248084,
- spittingCobra = 194407,
- volley = 194386,
- },
- debuffs = {
- barbedShot = 217200,
- },
- glyphs = {
-
- },
- talents = {
- animalCompanion = 267116,
- barrage = 120360,
- bloodshed = 321530,
- direBeast = 120679,
- killCleave = 378207,
- killerCobra = 199532,
- killerInstinct = 273887,
- oneWithThePack = 199528,
- scentOfBlood = 193532,
- stampede = 201430,
- stomp = 199530,
- thrillOfTheHunt = 257944,
- wailingArrow = 392060,
- },
- traits = {
- danceOfDeath = 274441,
- primalInstincts = 279806,
- rapidReload = 278530,
- },
- },
- -- Marksmanship
- [254] = {
- abilities = {
- },
- artifacts = {
- },
- buffs = {
- bulletstorm = 389020,
- bullseye = 204090,
- deadEye = 321461,
- deathblow = 378770,
- feignDeath = 5384,
- inTheRhythm = 272733,
- lethalShots = 260395,
- lockAndLoad = 194594,
- masterMarksman = 269576,
- preciseShots = 260242,
- razorFragments = 388993,
- salvo = 400456,
- steadyFocus = 193534,
- trickShots = 257622,
- trueshot = 288613,
- unerringVision = 274447,
- },
- debuffs = {
- huntersMark = 257284,
- serpentSting = 271788,
- },
- glyphs = {
-
- },
- interrupts = {
- counterShot = 147362,
- },
- talents = {
- aimedShot = 19434,
- barrage = 120360,
- bindingShackles = 321468,
- bulletstorm = 389019,
- bullseye = 204089,
- callingTheShots = 260404,
- carefulAim = 260228,
- chimaeraShot = 342049,
- crackShot = 321293,
- deathblow = 378769,
- eagletalonsTrueFocus = 389449,
- explosiveShot = 212431,
- fanTheHammer = 459794,
- focusedAim = 378767,
- heavyAmmo = 378910,
- hydrasBite = 260241,
- improvedSteadyShot = 321018,
- inTheRhythm = 407404,
- killShot = 53351,
- killZone = 459921,
- killerAccuracy = 378765,
- legacyOfTheWindrunners = 406425,
- lightAmmo = 378913,
- lockAndLoad = 194595,
- loneWolf = 155228,
- masterMarksman = 260309,
- multishot = 257620,
- nightHunter = 378766,
- penetratingShots = 459783,
- preciseShots = 260240,
- rapidFire = 257044,
- rapidFireBarrage = 459800,
- razorFragments = 384790,
- readiness = 389865,
- salvo = 400456,
- serpentstalkersTrickery = 378888,
- smallGameHunter = 459802,
- steadyFocus = 193533,
- streamline = 260367,
- surgingShots = 391559,
- tacticalReload = 400472,
- trickShots = 257621,
- trueshot = 288613,
- unerringVision = 386878,
- volley = 260243,
- wailingArrow = 459806,
- },
- traits = {
- focusedFire = 278531,
- inTheRhythm = 264198,
- rapidReload = 278530,
- steadyAim = 277651,
- surgingShots = 287707,
- unerringVision = 274444,
- },
- },
- -- Survival
- [255] = {
- abilities = {
- aspectOfTheEagle = 186289,
- carve = 187708,
- coordinatedAssault = 266779,
- harpoon = 190925,
- intimidation = 19577,
- killCommand = 259489,
- killShot = 320976,
- muzzle = 187707,
- pheromoneBomb = 270323, -- Wildfire Infusion
- raptorStrike = 186270,
- serpentSting = 259491,
- shrapnelBomb = 270335, -- Wildfire Infusion
- volatileBomb = 271045, -- Wildfire Infusion
- wakeUp = 210000,
- wildfireBomb = 259495,
- },
- artifacts = {
-
- },
- buffs = {
- aspectOfTheEagle = 186289,
- blurOfTalons = 277969,
- coordinatedAssault = 360952,
- exposedFlank = 252094, -- Tier 21
- mongooseFury = 259388,
- tipOfTheSpear = 260286,
- vipersVenom = 268552,
- madBombardier = 363805, -- Tier 28
- },
- debuffs = {
- bloodseeker = 259277,
- internalBleeding = 270343,
- serpentSting = 259491,
- shrapnelBomb = 270339,
- wildfireBomb = 269747,
- pheromoneBomb = 270332,
- },
- glyphs = {
-
- },
- talents = {
- alphaPredator = 269737,
- birdsOfPrey = 260331,
- bloodseeker = 260248,
- butchery = 212436,
- crackShot = 321293,
- flankingStrike = 269751,
- furyOfTheEagle = 203415,
- guerrillaTactics = 264332,
- hydrasBite = 260241,
- mongooseBite = 259387,
- steelTrap = 162488,
- termsOfEngagement = 265895,
- tipOfTheSpear = 260285,
- vipersVenom = 268501,
- wildfireInfusion = 271014,
- },
- traits = {
- blurOfTalons = 277653,
- latentPoison = 273283,
- primevalIntuition = 288570,
- upCloseAndPersonal = 278533,
- venomousFangs = 274590,
- wildernessSurvival = 279589,
- }
- },
- -- All
- Shared = {
- abilities = {
- -- Hunter Abilities
- arcaneShot = 185358,
- aspectOfTheChameleon = 61648,
- aspectOfTheCheetah = 186257,
- aspectOfTheTurtle = 186265,
- disengage = 781,
- eagleEye = 6197,
- exhilaration = 109304,
- eyeOfTheBeast = 321297,
- feignDeath = 5384,
- flare = 1543,
- fortitudeOfTheBear = 392956,
- freezingTrap = 187650,
- huntersMark = 257284,
- steadyShot = 56641,
- trackBeasts = 1494,
- trackDemons = 19878,
- trackDragonkin = 19879,
- trackElementals = 19880,
- trackGiants = 19882,
- trackHidden = 19885,
- trackHumanoids = 19883,
- trackMechanicals = 229533,
- trackUndead = 19884,
- wingClip = 195645,
- -- Pet Management
- beastLore = 1462,
- callPet = 9,
- callPet1 = 883,
- callPet2 = 83242,
- callPet3 = 83243,
- callPet4 = 83244,
- callPet5 = 83245,
- commandPet = 272651,
- dismissPet = 2641,
- feedPet = 6991,
- fetch = 125050,
- mendPet = 136,
- playDead = 209997,
- revivePet = 982,
- tameBeast = 1515,
- wakeUp = 210000,
- ----------------------------
- -- Pet - Basic Abilities ---
- ----------------------------
- bite = 17253, -- Basalisk, Bat, Beetle, Blood Beast, Boar, Carapid, Carrion Bird, Chimera, Core Hound, Crocolisk, Devilsaur, Direhorn, Dragonhawk, Feathermane, Fox, Hound, Hydra, Hyena, Lizard, Mechanical, Pterrordax, Ravager, Ray, Riverbeast, Serpent, Shale Beast, Spider, Stone Hound, Turtle, Warp Stalker, Water Strider, Wind Serpent, Wolf, Worm
- claw = 16827, -- Aqiri, Bear, Bird of Prey, Cat, Crab, Raptor, Rodent, Scorpid, Spirit Beast, Tallstrider
- dash = 61684, -- Speed Increase (ALL PETS)
- smack = 49966, -- Camel, Clefthoof, Courser, Crane, Gorilla, Gruffhorn, Mammoth, Monkey, Moth, Oxen, Scalehide, Sporebat, Stag, Toad, Wasp
- growl = 2649, -- Taunt (ALL PETS)
- ---------------------------
- -- Pet - Spec Abilities ---
- ---------------------------
- primalRage = 264667, -- Ferocity Dmg Cd
- -- survivalOfTheFittest = 264735, -- Tenacity Def Cd
- mastersCall = 53271, -- Cunning Freedom
- ------------------------------
- -- Pet - Special Abilities ---
- ------------------------------
- -- AOE
- burrowAttack = 93433, -- Worm
- froststormBreath = 92380, -- Chimera - Channeled Cone AOE
- -- Defense/Dodge
- agileReflexes = 160011, -- Fox - Dodge
- bristle = 263869, -- Boar - Defensive
- bulwark = 279410, -- Carapid - Defensive
- catlikeReflexes = 263892, -- Cat - Dodge
- defenseMatrix = 263868, -- Mechanical - Defensive
- dragonsGuile = 263887, -- Dragonhawk - Dodge
- featherFlurry = 263916, -- Feathermane - Dodge
- fleethoof = 341117, -- Courser - Dodge
- hardenCarapace = 90339, -- Beetle - Defensive
- obsidianSkin = 263867, -- Core Hound - Defensive
- primalAgility = 160044, -- Monkey - Dodge
- scaleShield = 263865, -- Scalehide - Defensive
- serpentSwiftness = 263904, -- Serpent - Dodge
- shellShield = 26064, -- Turtle - Defensive
- solidShell = 160063, -- Shale Beast - Defensive
- swarmOfFlies = 279336, -- Toad - Dodge
- wingedAgility = 264360, -- Wind Serpent - Dodge
- -- Dispel
- chiJiTranq = 344350, -- Crane
- naturesGrace = 344352, -- Stag
- netherEnergy = 344349, -- Ray -- netherEnergy
- serenityDust = 344353, -- Moth
- sonicScreech = 344348, -- Bat -- sonicScreech
- soothingWater = 344346, -- Water Strider
- spiritPulse = 344351, -- Spirit Beast -- spiritPulse
- sporeCloud = 344347, -- Sporebat
- -- Heal
- eternalGuardian = 267922, -- Stone Hound - Ressurect
- feast = 159953, -- Devilsaur - Heal (req dead humanoid/beast nearby)
- spiritmend = 90361, -- Spirit Beast - Heal
- -- Mortal Wounds Debuff
- acidBite = 263863, -- Hydra - Mortal Wounds Debuff
- bloodyScreech = 24423, -- Carrion Bird - Mortal Wounds Debuff
- deadlySting = 160060, -- Scorpid - Mortal Wounds Debuff
- gnaw = 263856, -- Rodent - Mortal Wounds Debuff
- gore = 263861, -- Direhorn - Mortal Wounds Debuff
- grievousBite = 279362, -- Lizard - Mortal Wounds Debuff
- gruesomeBite = 160018, -- Riverbeast - Mortal Wounds Debuff
- infectedBite = 263853, -- Hyena - Mortal Wounds Debuff
- monsterousBite = 54680, -- Devilsaur - Mortal Wounds Debuff
- ravage = 263857, -- Ravager - Mortal Wounds Debuff
- savageRend = 263854, -- Savage Rend - Mortal Wounds Debuff
- toxicSting = 263858, -- Wasp - Mortal Wounds Debuff
- -- Slow
- acidSpit = 263446, -- Worm - Slow
- ankleCrack = 50433, -- Crocolisk - Slow
- bloodBolt = 288962, -- Blood Beast - Slow
- dustCloud = 50285, -- Tallstrider - Slow
- frostBreath = 54644, -- Chimera - Slow
- furiousBite = 263840, -- Wolf - Slow
- lockJaw = 263423, -- Hound - Slow
- petrifyingGaze = 263841, -- Basilisk - Slow
- pin = 50245, -- Crab - Slow
- talonRend = 263852, -- Bird of Prey - Slow
- tendonRip = 160065, -- Aqiri - Slow
- trample = 341118, -- Mammoth - Slow
- warpTime = 35346, -- Warp Stalker - Slow
- webSpray = 160067, -- Spider - Slow
- -- Slow Fall
- updraft = 160007, -- Feathermane/Pterrordax - Slow Fall
- -- Stealth
- prowl = 24450, -- Stealth
- spiritWalk = 90328, -- Spirit Beast - Stealth
- -- Tricks / Play / Rest
- play = 90347, -- Fox
- restBear = 94019, -- Bear
- restRodent = 126364, -- Rodent
- trickCrane = 126259, -- Crane
- trickBirdOfPrey = 94022, -- Bird of Prey
- -- Water Walking
- surfaceTrot = 126311, -- Water Strider - Water Walking
- },
- animas = {
- soulforgeEmbers = 331197,
- },
- artifacts = {
-
- },
- buffs = {
- aspectOfTheTurtle = 186265,
- eagletalonsTrueFocus = 336851,
- feignDeath = 5384,
- flayersMark = 324156, -- Covenant
- killingFrenzy = 363665, -- T28 4pc
- mendPet = 136,
- nesingwarysTrappingApparatus = 336744,
- playDead = 209997,
- prowl = 24450,
- resonatingArrow = 356263, -- Covenant
- spiritWalk = 90328,
- surfaceTrot = 126311, -- Water Strider - Water Walking
- updraft = 160007, -- Feathermane/Pterrordax - Slow Fall
- volley = 260243,
- wildSpirits = 328231,
- },
- conduits = {
- bloodletting = 341440,
- marksmansAdvantage = 339264,
- },
- covenants = {
- flayedShot = 324149,
- resonatingArrow = 308491,
- wildSpirits = 328231,
- },
- debuffs = {
- bestialFerocity = 191413,
- freezingTrap = 3355,
- huntersMark = 257284,
- intimidation = 24934,
- latentPoison = 378015,
- mortalWounds = 115804, -- Pet Ability Debuff
- resonatingArrow = 308498, -- Covenant
- soulforgeEmbers = 331269, -- Covenant
- tarTrap = 135299,
- wildMark = 328275, -- Covenant
- },
- glyphs = {
-
- },
- interrupts = {
- freezingTrap = 187650,
- },
- runeforges = {
- bagOfMunitions = 356264,
- eagletalonsTrueFocus = 336849,
- nesingwarysTrappingApparatus = 336743,
- pouchOfRazorFragments = 356618,
- rylakstalkersConfoundingStrikes = 336901,
- secretsOfTheUnblinkingVigil = 336878,
- serpentstalkersTrickery = 336870,
- soulforgeEmbers = 336745,
- surgingShots = 336867,
- qaplaEredunWarOrder = 336830,
- },
- talents = {
- bindingShot = 109248,
- blackrockMunitions = 462036,
- bornToBeWild = 266921,
- burstingShot = 186387,
- camouflage = 199483,
- concussiveShot = 5116,
- counterShot = 147362,
- devilsaurTranquilizer = 459991,
- disruptiveRounds = 343244,
- emergencySalve = 459517,
- entrapment = 393344,
- ghillieSuit = 459466,
- highExplosiveTrap = 236776,
- huntersAvoidance = 384799,
- implosiveTrap = 462031,
- improvedKillShot = 343248,
- improvedTraps = 343247,
- intimidation = 19577,
- keenEyesight = 378004,
- kindlingFlare = 459506,
- kodoTranquilizer = 459983,
- loneSurvivor = 388039,
- misdirection = 34477,
- momentOfOpportunity = 459488,
- naturalMending = 270581,
- noHardFeelings = 459546,
- paddedArmor = 459450,
- pathfinding = 378002,
- posthaste = 109215,
- quickLoad = 378771,
- rejuvenatingWind = 385539,
- roarOfSacrifice = 53480,
- scareBeast = 1513,
- scatterShot = 213691,
- scoutsInstincts = 459455,
- scrappy = 459533,
- serratedTips = 459502,
- survivalOfTheFittest = 264735,
- specializedArsenal = 459542,
- tarTrap = 187698,
- tarcoatedBindings = 459460,
- territorialInstincts = 459507,
- trailblazer = 199921,
- tranquilizingShot = 19801,
- triggerFinger = 459534,
- unnaturalCauses = 459527,
- wildernessMedicine = 343242,
- },
- talentsHeroic = {
- -- Dark Ranger
- blackArrow = 430703,
- darkChains = 430712,
- darkEmpowerment = 430718,
- deathShade = 430711,
- darknessCalls = 430722,
- embraceTheShadows = 430704,
- graveReaper = 430719,
- overshadow = 430716,
- shadowErasure = 430720,
- shadowHounds = 430707,
- shadowLash = 430717,
- shadowSurge = 430714,
- smokeScreen = 430709,
- witheringFire = 430715,
- -- Pack Leader
- beastOfOpportunity = 445700,
- corneredPrey = 445702,
- coveringFire = 445715,
- cullTheHerd = 445717,
- denRecovery = 445710,
- frenziedTear = 445696,
- furiousAssault = 445699,
- howlOfThePack = 445707,
- packAssault = 445721,
- packCoordination = 445505,
- scatteredPrey = 445768,
- tirelessHunt = 445701,
- viciousHunt = 445404,
- wildAttacks = 445708,
- -- Sentinel
- catchOut = 451516,
- crescentSteel = 451530,
- extrapolatedShots = 450374,
- eyesClosed = 450381,
- dontLookBack = 450373,
- invigoratingPulse = 450379,
- lunarStorm = 450385,
- overwatch = 450384,
- releaseAndReload = 450376,
- sentinel = 450369,
- sentinelPrecision = 450375,
- sentinelWatch = 451546,
- sideline = 450378,
- symphonicArsenal = 450383,
- },
- },
- },
- MAGE = {
- -- Arcane
- [62] = {
- abilities = {
- alterTime = 342245,
- arcaneBarrage = 44425,
- arcaneBlast = 30451,
- arcaneFamiliar = 205022,
- arcaneMissiles = 5143,
- arcaneOrb = 153626,
- arcanePower = 12042,
- chargedUp = 205032,
- conjuremanaGem = 759,
- displacement = 212801,
- erosion = 205039,
- evocation = 12051,
- -- fireblast = 519836,
- greaterInvisibility = 110959,
- markOfAluneth = 224968,
- netherTempest = 114923,
- presenceofMind = 205025,
- prismaticBarrier = 235450,
- runeofPower = 116011,
- slow = 31589,
- supernova = 157980,
- touchOfTheMagi = 321507,
- },
- artifacts = {
- aegwynnsAscendance = 187680,
- aegwynnsFury = 187287,
- aegwynnsInperative = 187264,
- aegwynnsIntensity = 238054,
- aegwynnsWrath = 187321,
- alunethsAvarice = 238090,
- ancientPower = 214917,
- arcanePurification = 187313,
- arcaneRebound = 188006,
- blastingRod = 187258,
- cracklingEnergy = 187310,
- etherealSensitivity = 187276,
- everywhereAtOnce = 187301,
- forceBarrier = 210716,
- intensityOfTheTirisgarde = 241121,
- markOfAluneth = 224968,
- mightOfTheGuardians = 187318,
- ruleOfThrees = 215463,
- sloooowDown = 210730,
- timeAndSpace = 238126,
- torrentialBarrage = 187304,
- touchOfTheMagi = 210725,
- },
- buffs = {
- arcaneCharge = 36032,
- arcaneFamiliar = 210126,
- arcanePower = 12042,
- -- arcaneCharge = 36032,
- evocation = 12051,
- expandedPotential = 327495,
- arcaneMissles = 79683,
- presenceOfMind = 205025,
- prismaticBarrier = 235450,
- rhoninsAssaultingArmwraps = 208081,
- runeofPower = 116011,
- ruleOfThrees = 264774,
- brainStorm = 273330,
- clearcasting = 263725,
- },
- debuffs = {
- arcaneCharge = 36032,
- netherTempest = 114923,
- touchoftheMagi = 210824,
-
-
- },
- glyphs = {
-
- },
- talents = {
- arcaneEcho = 342231,
- amplification = 236628,
- arcaneFamiliar = 205022,
- arcaneOrb = 153626,
- chronoShift = 235711,
- mastersOfTime = 342249,
- --erosion = 205039,
- netherTempest = 114923,
- overpowered = 155147,
- resonance = 205028,
- reverberate = 281482,
- rulesOfThrees = 264354,
- slipstream = 236457,
- supernova = 157980,
- timeAnomaly = 210805
- --temporalFlux = 234302,
- --wordsOfPower = 205035,
- },
- traits = {
- anomalousImpact = 279867,
- arcanePressure = 274594,
- arcanePummeling = 270669,
- brainStorm = 273326,
- explosiveEcho = 278537,
- galvanizingSpark = 278536,
- },
- },
- -- Fire
- [63] = {
- abilities = {
- alterTime = 108978,
- blazingBarrier = 235313,
- blastWave = 157981,
- cinderstorm = 198929,
- combustion = 190319,
- dragonsBreath = 31661,
- disciplinaryCommand = 327365,
- fireball = 133,
- fireBlast = 108853,
- fireBlast2 = 319836,
- flameOn = 205029,
- flamestrike = 2120,
- infernalCascade = 336821,
- livingBomb = 44457,
- meteor = 153561,
- phoenixFlames = 257541,
- pyroblast = 11366,
- shiftingPower = 314791,
- scorch = 2948,
- soulIgnition = 345211,
- },
- artifacts = {
- aftershocks = 194431,
- phoenixReborn = 215773,
- },
- buffs = {
- blazingBarrier = 235313,
- blasterMaster = 274598,
- combustion = 190319,
- disciplinaryArcane = 327369,
- disciplinaryCommand = 327365,
- disciplinaryCommandFrost = 327366,
- disciplinaryCommandFire = 327368,
- firestorm = 333097,
- heatingUp = 48107,
- hotStreak = 48108,
- iceFloes = 108839,
- infernalCascade = 336832,
- kaelthasUltimateAbility = 209455,
- preheat = 273333,
- pyroclasm = 269651,
- shiftingPower = 314791,
- sunKingsBlessings = 333313,
- soulIgnition = 345211,
- },
- debuffs = {
- ignite = 12654,
- meteorBurn = 155158,
- cauterized = 87024,
- },
- glyphs = {
+-- Defer spell list loading until after unlockers are ready
+function br.lists:loadExpansionSpells()
+ -- br._G.print("|cff00FFFFBadRotations:|r loadExpansionSpells() called")
- },
- talents = {
- alexstraszasFury = 235870,
- blastWave = 157981,
- blazingSoul = 235365,
- --cinderstorm = 198929,
- conflagration = 205023,
- --controlledBurn = 205033,
- freneticSpeed = 236058,
- firestarter = 205026,
- flameOn = 205029,
- flamePatch = 205037,
- kindling = 155148,
- livingBomb = 44457,
- meteor = 153561,
- focusMagic = 321358,
- fromTheAshes = 342344,
- pyromaniac = 205020,
- pyroclasm = 269650,
- searingTouch = 269644,
- },
- covenants = {
- shiftingPower = 314791,
- },
- traits = {
- preheat = 273332,
- blasterMaster = 274596,
- },
- conduits = {
- infernalCascade = 336821,
- },
- runeforges = {
- firestorm = 333097,
- grislyIcicle = 333393,
- disciplinaryCommand = 327365,
- sunKingsBlessings = 333313
- }
- },
- -- Frost
- [64] = {
- abilities = {
- alterTime = 108978,
- blizzard = 190356,
- coldSnap = 235219,
- coneOfCold = 120,
- --fireBlast = 108853, - Fire Mage version, Frost should not have this and already listed fireBlast below
- flurry = 44614,
- freeze = 231596,
- frostbolt = 116,
- frostBomb = 112948,
- frozenOrb = 84714,
- frozenTouch = 205030,
- fireBlast = 319836,
- iceBarrier = 11426,
- iceFloes = 108839,
- iceForm = 198144,
- iceLance = 30455,
- iceNova = 157997,
- icyVeins = 12472,
- petFreeze = 33395,
- rayOfFrost = 205021,
- runeofPower = 116011,
- removeCurse = 475,
- summonWaterElemental = 31687,
- waterbolt = 31707,
- },
- artifacts = {
- -- icyHand = 220817,
- },
- buffs = {
- brainFreeze = 190446,
- concentratedCoolness = 198148,
- freezingRain = 270232,
- frostBomb = 112948,
- fingersOfFrost = 44544,
- icyVeins = 12472,
- chainReaction = 195418,
- zannesuJourney = 226852,
- timeWarp = 80353,
- iceFloes = 108839,
- iceForm = 198144,
- iceBarrier = 11426,
- icicles = 205473,
- frozenMass = 242253,
- },
- debuffs = {
- chainsOfIce = 65173,
- chilled = 205708,
- frostBomb = 112948,
- frostNova = 122,
- iceNova = 157997,
- wintersChill = 228358,
- },
- glyphs = {
-
- },
- talents = {
- --articGale = 205038,
- boneChilling = 205027,
- cometStorm = 153595,
- chainReaction = 278309,
- ebonbolt = 257537,
- --frostBomb = 112948,
- frozenTouch = 205030,
- frigidWinds = 235224,
- freezingRain = 270233,
- focusMagic = 321358,
- glacialSpike = 199786,
- glacialInsulation = 235297,
- iceNova = 157997,
- iceFloes = 108839,
- lonelyWinter = 205024,
- rayOfFrost = 205021,
- splittingIce = 56377,
- thermalVoid = 155149,
- },
- },
- -- All
- Shared = {
- abilities = {
- fireBlast = 319836,
- frostbolt = 116,
- arcaneIntellect = 1459,
- arcaneExplosion = 1449,
- blink = 1953,
- counterspell = 2139,
- conjureRefreshment = 190336,
- -- fireBlast = 319836,
- frostBolt = 116,
- frostNova = 122,
- iceBlock = 45438,
- invisibility = 66,
- mirrorImage = 55342,
- polymorph = 118,
- removeCurse = 475,
- ringOfFrost = 113724,
- runeOfPower = 116011,
- --shimmer = 212653,
- slowFall = 130,
- spellsteal = 30449,
- teleportExodar = 32271,
- timeWarp = 80353,
- waterJet = 135029,
- },
- artifacts = {
-
- },
- buffs = {
- arcaneIntellect = 1459,
- disciplinaryCommand = 327365,
- expandedPotential = 327489,
- focusMagic = 321358,
- freezingWinds = 327364,
- iceBlock = 45438,
- incantersFlow = 1463,
- slowFall = 130,
- slickIce = 327509,
- runeOfPower = 116014,
- },
- conduits = {
-
- },
- covenants = {
- deathborne = 324220,
- mirrorsOfTorment = 314793,
- radiantSpark = 307443,
- shiftingPower = 314791,
- },
- debuffs = {
- frostNova = 122,
- mirrorsOfTorment = 314793,
- },
- glyphs = {
-
- },
- runeforges = {
- disciplinaryCommand = 327365,
- sunKingsBlessings = 333313,
- grislyIcicle = 333393,
- firestorm = 333097,
-
- },
- talents = {
- incantersFlow = 1463,
- iceWard = 205036,
- focusMagic = 321358,
- ringOfFrost = 113724,
- runeOfPower = 116011,
- shimmer = 212653,
- --unstableMagic = 157976,
- },
- },
- },
- MONK = {
- -- Brewmaster
- [268] = {
- abilities = {
- blackoutKick = 205523,
- breathOfFire = 115181,
- celestialBrew = 322507,
- clash = 324312,
- invokeNiuzao = 132578,
- kegSmash = 121253,
- purifyingBrew = 119582,
- spinningCraneKick = 322729,
- zenMeditation = 115176
- },
- artifacts = {
-
- },
- buffs = {
- eyeOfTheTiger = 196608,
- ironskinBrew = 215479,
- blackoutCombo = 228563,
- purifiedChi = 325092,
- rushingJadeWind = 116847,
- zenMeditation = 115176,
- celestialBrew = 322507,
- invokeNiuzao = 132578,
- counterStrike = 383800,
- hitScheme = 383696,
- shuffle = 322120,
- invokeNiuzaoTheBlackOx = 132578,
- weaponsOfOrder = 387184,
- charredPassions = 386965,
- bonedustBrew = 386276,
- },
- debuffs = {
- breathOfFire = 146222,
- moderateStagger = 124274,
- heavyStagger = 124273,
- kegSmash = 121253,
- weaponsOfOrder = 387184,
- bonedustBrew = 386276,
-
- },
- glyphs = {
-
- },
- talents = {
- blackoutCombo = 196736,
- blackOxBrew = 115399,
- blackOxStatue = 115399,
- bobAndWeave = 280515,
- celestialFlame = 325177,
- explodingKeg = 325153,
- eyeOfTheTiger = 196607,
- healingElixir = 122281,
- highTolerance = 196737,
- lightBrewing = 325093,
- rushingJadeWind = 116847,
- specialDelivery = 196730,
- summonBlackOxStatue = 115315,
- detox = 218164,
- improvedPurifyingBrew = 343743,
- invokeNiuzaoTheBlackOx = 132578,
- improvedInvokeNiuzaoTheBlackOx = 322740,
- weaponsOfOrder = 387184,
- charredPassions = 386965,
- pressTheAdvantage = 418359,
- bonedustBrew = 386276,
- },
- },
- -- Mistweaver
- [270] = {
- abilities = {
-
- envelopingMist = 124682,
- essenceFont = 191837,
- invokeYulon = 322118,
- reawaken = 212051,
- renewingMist = 115151,
- revival = 115310,
- risingSunKick = 107428,
- soothingMist = 115175,
- thunderFocusTea = 116680,
- invokeYulonTheJadeSerpent = 322118,
- invokeChiJiTheRedCrane = 325197,
- fortifyingBrew = 243435,
- zenFocusTea = 209584,
- transcendenceTransfer = 119996,
- sheilunsGift = 399491,
- detox = 115450,
- manaTea = 115294,
- },
- artifacts = {
- },
- buffs = {
- weaponsOfOrder = 310454,
- fortifyingBrew = 243435,
- soothingMist = 115175,
- renewingMist = 115151,
- envelopingMist = 124682,
- thunderFocusTea = 116680,
- lifeCyclesEnvelopingMist = 197919,
- lifeCyclesVivify = 197916,
- surgeOfMist = 246328,
- danceOfMist = 247891,
- upliftTrance = 197206,
- refreshingJadeWind = 196725,
- lifeCocoon = 116849,
- transcendence = 101643,
- tigersLust = 116841,
- teachingsOfTheMonastery = 202090,
- diffuseMagic = 122783,
- dempenHarm = 122278,
- innervate = 29166,
- symbolOfHope = 64901,
- wayOfTheCrane = 216113,
- essenceFont = 191840,
- risingMist = 22170,
- soothingMistJadeStatue = 198533,
- envelopingBreath = 325209,
- invokeChiJiTheRedCrane = 343820,
- ancientTeachingOfTheMonastery = 347553,
- fallenOrder = 326860,
- vivaciousVivification = 392883,
- rushingJadeWind = 116847,
- ancientTeachings = 388023,
- sheilunsGift = 399491,
- manaTea = 115867,
- },
- debuffs = {
- mysticTouch = 113746,
- },
- glyphs = {
- },
- talents = {
- diffuseMagic = 122783,
- focusedThunder = 197895,
- invokeChiJiTheRedCrane = 325197,
- lifecycles = 197915,
- mistWrap = 197900,
- refreshingJadeWind = 196725,
- risingMist = 274909,
- songOfChiJi = 198898,
- summonJadeSerpentStatue = 115313,
- upwelling = 274963,
- faelineStomp = 388193,
- zenPulse = 124081,
- ancienTeachings = 388023,
- invokeYulonTheJadeSerpent = 322118,
- lifeCocoon = 116849,
- sheilunsGift = 399491,
- spearHandStrike = 116705,
- --manaTea = 197908,
-
- },
- runeforges = {
- ancientTeachingsOfTheMonastery = 337172,
- tearOfMorning = 337473,
- }
- },
- -- Windwalker
- [269] = {
- abilities = {
- disable = 116095,
- fistsOfFury = 113656,
- flyingSerpentKick = 101545,
- flyingSerpentKickEnd = 115057,
- fortifyingBrew = 243435,
- invokeXuenTheWhiteTiger = 123904,
- risingSunKick = 107428,
- stormEarthAndFire = 137639,
- stormEarthAndFireFixate = 221771,
- touchOfKarma = 122470,
- grappleWeapon = 233759,
- fallenOrder = 326860,
- flashcraft = 324631,
- summonWhiteTigerStatue = 388686,
- },
- artifacts = {
-
- },
- buffs = {
- alphaTiger = 287504,
- fortifyingBrew = 243435,
- invokersDelight = 338321,
- blackoutKick = 116768,
- chiEnergy = 337571,
- danceOfChiJi = 325202,
- hitCombo = 196741,
- pressurePoint = 247255,
- rushingJadeWind = 116847,
- serenity = 152173,
- stormEarthAndFire = 137639,
- swiftRoundhouse = 278710,
- theEmperorsCapacitor = 235054,
- touchOfKarma = 122470,
- transferThePower = 195321,
- whirlingDragonPunch = 152175,
- fallenOrder = 326860,
- bonedustBrew = 325216,
- teachingsOfTheMonastery = 202090,
- },
- debuffs = {
- disable = 116095,
- disableRoot = 116706,
- markOfTheCrane = 228287,
- mortalWounds = 115804,
- risingFist = 242259,
- skyreachExhaustion = 337341,
- jadefireBrand = 395414,
- },
- glyphs = {
- glyphOfRisingTigerKick = 125151,
- },
- talents = {
- ascension = 115396,
- danceOfChiJi = 325201,
- diffuseMagic = 122783,
- eyeOfTheTiger = 196607,
- hitCombo = 196740,
- rushingJadeWind = 116847,
- serenity = 152173,
- spiritualFocus = 280197,
- strikeOfTheWindlord = 392983,
- whirlingDragonPunch = 152175,
- skyreach = 392991,
- skytouch = 405044,
- detox = 218164,
- jadefireHarmony = 391412,
- bonedustBrew = 386276,
- stormEarthAndFire = 137639,
- jadefireStomp = 388193,
-
- },
- traits = {
- gloryOfTheDawn = 288634,
- openPalmStrikes = 279918,
- swiftRoundhouse = 277669,
- },
- runeforges = {
- fatalTouch = 337296
- }
- },
- -- All
- Shared = {
- abilities = {
- blackoutKick = 100784,
- cracklingJadeLightning = 117952,
- expelHarm = 322101,
- legSweep = 119381,
- provoke = 115546,
- resuscitate = 115178,
- roll = 109132,
- spinningCraneKick = 101546,
- tigerPalm = 100780,
- touchOfDeath = 322109,
- touchOfFatality = 169340,
- vivify = 116670,
- zenFlight = 125883,
- zenPilgrimage = 126892,
- zenPilgrimageReturn = 126895,
- },
- artifacts = {
-
- },
- buffs = {
- theEmperorsCapacitor = 235054,
- transcendence = 101643,
- weaponsOfOrder = 328908, --310454,
- weaponsOfOrderWW = 310454, --311054,
- prideful = 340880,
- vivaciousVivification = 392883,
- powerStrikes = 129914, --bonus chi proc buff
- hitCombo = 196741,
- kicksOfFlowingMomentum = 394944,
- blackoutReinforcement = 424454,
- },
- conduits = {
- calculatedStrikes = 336526,
- coordinatedOffensive = 336598,
- pustuleEruption = 351094
- },
- covenants = {
- bonedustBrew = 325216,
- faelineStomp = 327104,
- fallenOrder = 326860,
- weaponsOfOrder = 310454,
- },
- debuffs = {
- bonedustBrew = 325216,
- },
- glyphs = {
-
- },
- talents = {
- bounceBack = 389577,
- calmingPresence = 388664,
- celerity = 115173,
- chiBurst = 123986,
- chiTorpedo = 115008,
- chiWave = 115098,
- closeToHeart = 389574,
- dampenHarm = 122278,
- disable = 116095,
- elusiveMists = 388681,
- escapeFromReality = 394110,
- expeditiousFortification = 388813,
- fastFeet = 388809,
- fatalTouch = 394123,
- ferocityOfXuen = 388674,
- generousPour = 389575,
- graceOfTheCrane = 388811,
- hastyProvocation = 328670,
- improvedParalysis = 344359,
- improvedRoll = 328669,
- improvedTouchOfDeath = 322113,
- ironshellBrew = 388814,
- paralysis = 115078,
- profoundRebuttal = 392910,
- resonantFists = 389578,
- ringOfPeace = 116844,
- risingSunKick = 107428,
- saveThemAll = 389579,
- spearHandStrike = 116705,
- strengthOfSpirit = 387276,
- summonWhiteTigerStatue = 388686,
- tigerTailSweep = 264348,
- tigersLust = 116841,
- transcendence = 101643,
- vigorousExpulsion = 392900,
- vivaciousVivication = 388812,
- windwalking = 157411,
- },
- },
- },
- PALADIN = {
- -- Holy
- [65] = {
- abilities = {
- absolution = 212056,
- auraMastery = 31821,
- barrierOfFaith = 148039,
- beaconOfFaith = 156910,
- beaconOfLight = 53563,
- beaconOfVirtue = 200025,
- blessingOfAutumn = 388010,
- blessingOfSpring = 388013,
- blessingOfSummer = 388007,
- blessingOfWinter = 388011,
- cleanse = 4987,
- divineProtection = 498,
- holyLight = 82326,
- holyShock = 20473,
- lightOfDawn = 85222,
- lightOfTheMartyr = 183998,
- --lightsHammer = 114158,
-
- },
- artifacts = {
-
- },
- buffs = {
- --sealOfClarity = 384810,
- avengingCrusader = 216331,
- auraMastery = 31821,
- avengingWrath = 31884,
- avengingWrathCrit = 294027,
- afterimage = 400745,
- beaconOfLight = 53563,
- beaconOfFaith = 156910,
- blessingOfSacrifice = 6940,
- beaconOfVirtue = 200025,
- --bestowFaith = 223306,
- --glimmerOfLight = 287280,
- blessingOfDawn = 337747,
- blessingOfDusk = 385126,
- divineProtection = 498,
- divinePurpose = 223819,
- --ferventMartyr = 223316,
- infusionOfLight = 54149,
- innervate = 29166,
- --ruleOfLaw = 214202,
- liberation = 461471,
- --theLightSaves = 200423,
- vindicator = 200376,
- symbolOfHope = 64901,
- divineSteed = 254474,
- --maraadsBreath = 340459,
- unendingLight = 394709,
- untemperedDedication = 339990,
- veneration = 392939,
- },
- debuffs = {
- judgement = 214222,
- judgmentOfLight = 196941,
- --glimmerOfLight = 287280,
- consecration = 204242,
- dawnWillCome = 364851,
-
- },
- glyphs = {
-
- },
- talents = {
- avengingCrusader = 216331,
- --awakening = 248033,
- beaconOfFaith = 156910,
- beaconOfVirtue = 200025,
- --bestowFaith = 223306,
- blessingOfSummer = 388007,
- breakingDawn = 387879,
- crusadersMight = 196926,
- --glimmerOfLight = 325966,
- holyPrism = 114165,
- judgmentOfLight = 183778,
- --lightsHammer = 114158,
- --ruleOfLaw = 214202,
- --sanctifiedWrath = 53376,
- savedByTheLight = 157047,
- divineToll = 375576,
- },
- traits = {
- graceoftheJusticar = 278593,
- indomitableJustice = 275496,
- },
- runeforges = {
- shadowbreaker = 337812,
- shockBarrier = 337825,
- maraadsDyingBreath = 340458,
- }
- },
- -- Protection
- [66] = {
- abilities = {
- ardentDefender = 31850,
- avengersShield = 31935,
- blessedHammer = 229976,
- cleanseToxins = 213644,
- divineProtection = 498,
- guardianOfAncientKings = 86659,
- hammerOfTheRighteous = 53595,
- judgment = 275779,
- judgmentsOfTheWise = 105424,
- rebuke = 96231,
- righteousFury = 25780,
- },
- artifacts = {
-
- },
- buffs = {
- ardentDefender = 31850,
- avengingWrath = 31884,
- blessingOfSpellwarding = 204018,
- bulwarkOfOrder = 209388,
- consecration = 188370,
- divineShield = 642,
- divinePurpose = 223819,
- guardianOfAncientKings = 86659,
- shieldOfTheRighteous = 132403,
- shiningLight = 327510,
- avengersValor = 197561,
- divineSteed = 254474,
- royalDecree = 340147,
- },
- debuffs = {
- blessedHammer = 204301,
- judgmentOfLight = 196941,
- judgment = 197277,
- },
- glyphs = {
-
- },
- talents = {
- layOnHands = 633,
- blessedHammer = 204019,
- hammerOfTheRighteous = 53595,
- innerLight = 386568,
- redoubt = 280373,
- holyShield = 152261,
- grandCrusader = 85043,
- -- shiningLight = 321136, -- No longer a talent?
- consecratedGround = 204054,
- improvedLayOnHands = 393027,
- inspiringVanguard = 393022,
- ardentDefender = 31850,
- barricadeOfFaith = 385726,
- consecrationInFlame = 379022,
- improvedHolyShield = 393030,
- sanctuary = 379021,
- bulwarkOfOrder = 209389,
- improvedArdentDefender = 393114,
- blessingOfSpellwarding = 204018,
- lightOfTheTitans = 378405,
- strengthInAdversity = 393071,
- crusadersResolve = 380188,
- tyrsEnforcer = 378285,
- relentlessInquisitor = 383388,
- aveningWrathMight = 31884,
- sentinel = 389539,
- handOTheProtector = 315924,
- strengthOfConviction = 379008,
- resoluteDefender = 385422,
- bastionOfLight = 378974,
- guardianOfAncientKings = 86659,
- crusadersJudgement = 204023,
- uthersCounsel = 378425,
- focusedEnmity = 378845,
- soaringShield = 378457,
- giftOfTheGoldenValkyr = 378279,
- eyeOfTyr = 387174,
- righteousProtector = 204074,
- faithInTheLight = 379043,
- ferrenMarcussFervor = 378762,
- faithsArmor = 379017,
- finalStand = 204077,
- divineToll = 375576,
- momentOfGlory = 327193,
- bulwarkOfTheRighteousFury = 386653,
- divineResonance = 386738,
- quickenedInvocations = 379391,
- },
- traits = {
- bulwarkOfLight = 272976,
- },
- },
- -- Retribution
- [70] = {
- abilities = {
- divineProtection = 403876,
- templarSlash = 406647,
- templarStrike = 407480,
- templarsVerdict = 85256,
- },
- artifacts = {
-
- },
- buffs = {
- crusade = 231895,
- divineArbiter = 406975,
- divinePurpose = 223819,
- divineRight = 278523,
- empyreanLegacy = 387178,
- empyreanPower = 326733, --286393,
- finalVerdict = 414949,
- selflessHealer = 114250,
- templarStrikes = 406648,
- },
- debuffs = {
- executionSentence = 267798,
- expurgation = 383346,
- finalReckoning = 343721,
- },
- glyphs = {
- glyphOfWingedVengeance = 57979,
- },
- talents = {
- abjudication = 406157, --
- aegisOfProtection = 403654, --
- artOfWar = 406064, --
- avengingWrathMight = 31884, --
- bladeOfJustice = 184575, --
- bladeOfVengeance = 403826, --
- bladesOfLight = 403664, --
- blessedChampion = 403010, --
- boundlessJudgment = 405278, --
- burningCrusade = 405289, --
- consecratedBlade = 404834, --
- consecratedGround = 204054, --
- crusade = 231895, --
- crusadingStrikes = 404542, --
- divineArbiter = 404306, --
- divineAuxiliary = 406158, --
- divineHammer = 198034, --
- divineStorm = 53385, --
- divineWrath = 406872, --
- empyreanLegacy = 387170, --
- empyreanPower = 326732, --
- executionSentence = 343527, --
- executionersWill = 406940, --
- expurgation = 383344, --
- finalReckoning = 343721, --
- finalVerdict = 383328, --
- guidedPrayer = 404357, --
- heartOfTheCrusader = 406154, --
- highlordsJudgment = 404512, --
- holyBlade = 383342, --
- improvedBladeOfJustice = 403745, --
- improvedJudgment = 405461, --
- inquisitorsIre = 403975, --
- judgeJuryAndExecutioner = 405607, --
- judgmentOfJustice = 403495, --
- jurisdiction = 402971, --
- justicarsVengeance = 215661, --
- lightOfJustice = 404436, --
- lightsCelerity = 403698, --
- penitence = 403026, --
- righteousCause = 402912, --
- rushOfLight = 407067, --
- sanctify = 382536, --
- searingLight = 404540, --
- seethingFlames = 405355, --
- shieldOfVengeance = 184662, --
- swiftJustice = 383228, --
- tempestOfTheLightbringer = 383396, --
- templarStrikes = 406646, --
- truthsWake = 403696, --
- vanguardOfJustice = 406545, --
- vanguardsMomentum = 383314, --
- wakeOfAshes = 255937, --
- zealotsFervor = 403509, --
- },
- traits = {
- divineRight = 278523,
- },
- },
- -- All
- Shared = {
- abilities = {
- arcaneTorrent = 155145, --
- consecration = 26573, --
- crusaderAura = 32223, --
- crusaderStrike = 35395, --
- divineShield = 642, --
- flashOfLight = 19750, --
- hammerOfJustice = 853, --
- handOfReckoning = 62124, --
- intercession = 391054, --
- judgment = 20271, --
- redemption = 7328, --
- retributionAura = 183435, --
- senseUndead = 5502, --
- shieldOfTheRighteous = 53600, --
- wordOfGlory = 85673, --
- },
- artifacts = {
-
- },
- buffs = {
- avengingWrath = 31884,
- consecration = 26573,
- concentrationAura = 317920,
- crusaderAura = 32223,
- devotionAura = 465,
- divineResonance = 384029,
- divineShield = 642,
- divineSteed = 221883,
- echoesOfWrath = 423590,
- blessingOfProtection = 1022,
- holyAvenger = 105809,
- retributionAura = 183435,
- seraphim = 152262,
- vanquishersHammer = 328204,
- },
- conduits = {
- expurgation = 339371,
- templarsVindication = 339531,
- },
- covenants = {
- --ashenHallow = 316958,
- --blessingOfTheSeasons = 328278,
- --blessingOfAutumn = 388010,
- --blessingOfSpring = 388013,
- --blessingOfSummer = 388007,
- --blessingOfWinter = 388011,
- --vanquishersHammer = 328204,
- },
- debuffs = {
- blindingLight = 105421,
- forbearance = 25771,
- hammerOfJustice = 853,
- judgment = 197277,
- turnEvil = 10326,
- handOfReckoning = 62124,
- },
- glyphs = {
- glyphOfFireFromHeavens = 57954,
- glyphOfPillarOfLight = 146959,
- glyphOfTheLuminousCharger = 89401,
- glyphOfTheQueen = 212642,
- },
- talents = {
- afterimage = 385414, --
- aurasOfSwiftVengeance = 385639, --
- aurasOfTheResolute = 385633, --
- avengingWrath = 31884, --
- blessingOfFreedom = 1044, --
- blessingOfProtection = 1022, --
- blessingOfSacrifice = 6940, --
- blindingLight = 115750, --
- cavalier = 230332, --
- cleanseToxins = 213644, --
- crusadersReprieve = 403042, --
- divinePurpose = 408459, --
- divineResonance = 384027, --
- divineSteed = 190784, --
- divineToll = 375576, --
- fadingLight = 405768, --
- faithsArmor = 406101, --
- fistOfJustice = 234299, --
- goldenPath = 377128, --
- greaterJudgment = 231663, --
- hammerOfWrath = 24275, --
- healingHands = 326734, --
- holyAegis = 385515, --
- improvedBlessingOfProtection = 384909, --
- incandescence = 385464, --
- judgmentOfLight = 183778, --
- justification = 377043, --
- layOnHands = 633, --
- lightforgedBlessing = 403479, --
- obduracy = 385427, --
- ofDuskAndDawn = 385125, --
- punishment = 403530, --
- quickenedInvocation = 379391, --
- rebuke = 96231, --
- recompense = 384914, --
- repentance = 20066, --
- sacrificeOfTheJust = 384820, --
- sanctifiedPlates = 402964, --
- sealOfAlacrity = 385425, --
- sealOfMercy = 384897, --
- sealOfMight = 385450, --
- sealOfOrder = 385129, --
- sealOfTheCrusader = 385728, --
- seasonedWarhorse = 376996, --
- strengthOfConviction = 379008, --
- touchOfLight = 385349, --
- turnEvil = 10326, --
- unboundFreedom = 305394, --
- unbreakableSpirit = 114154, --
- vengefulWrath = 406835, --
- },
- runeforges = {
- finalVerdict = 337247,
- theMadParagon = 337594,
- theMagistratesJudgment = 337681,
- vanguardsMomentum = 337638,
- }
- },
- },
- PRIEST = {
- -- Discipline
- [256] = {
- abilities = {
- angelicFeather = 121536,
- --clarityOfWill = 152118,
- divineStar = 110744,
- desperatePrayer = 19236,
- evangelism = 246287,
- flashHeal = 2061,
- halo = 120517,
- holyNova = 132157,
- leapOfFaith = 73325,
- mindbender = 123040,
- mindBlast = 8092,
- mindControl = 205364,
- mindSear = 48045,
- mindSoothe = 453,
- mindVision = 2096,
- painSuppression = 33206,
- penance = 47540,
- --plea = 200829,
- powerInfusion = 10060,
- powerWordBarrier = 62618,
- powerWordRadiance = 194509,
- powerWordShield = 17,
- powerWordSolace = 129250,
- psychicScream = 8122,
- purgeTheWicked = 204197,
- purify = 527,
- rapture = 47536,
- schism = 214621,
- shadowfiend = 34433,
- shadowMend = 186263,
- shiningForce = 204263,
- sinsOfTheMany = 198076,
- spiritShell = 109964,
- smite = 585,
- },
- artifacts = {
- --sinsOfTheMany = 198074,
- },
- buffs = {
- angelicFeather = 121557,
- atonement = 194384,
- bodyAndSoul = 65081,
- borrowedTime = 197763,
- depthOfTheShadows = 275544,
- --clarityOfWill = 152118,
- innervate = 29166,
- overloadedWithLight = 223166,
- penitent = 246519,
- --powerInfusion = 10060,
- powerOfTheDarkSide = 198069,
- powerWordShield = 17,
- rapture = 47536,
- speedOfThePious = 197767,
- spiritShell = 109964,
- symbolOfHope = 64901,
- },
- debuffs = {
- weakenedSoul = 6788,
- purgeTheWicked = 204213,
- schism = 214621,
- smite = 585,
- },
- glyphs = {
-
- },
- talents = {
- angelicFeather = 121536,
- bodyAndSoul = 64129,
- castigation = 193134,
- --clarityOfWill = 152118,
- contrition = 197419,
- divineStar = 110744,
- dominantMind = 205367,
- evangelism = 246287,
- --grace = 200309,
- halo = 120517,
- lenience = 238063,
- masochism = 193063,
- mindbender = 123040,
- --powerInfusion = 10060,
- powerWordSolace = 129250,
- purgeTheWicked = 204197,
- psychicVoice = 196704,
- schism = 214621,
- shadowCovenant = 314867,
- shieldDiscipline = 197045,
- spiritShell = 109964,
- sinsOfTheMany = 280391,
- shiningForce = 204263,
- --thePenitent = 200347,
- twistOfFate = 265259,
- },
- traits = {
- giftOfForgiveness = 277680,
- },
- },
- -- Holy
- [257] = {
- abilities = {
- angelicFeather = 121536,
- bodyAndMind = 214121,
- circleOfHealing = 204883,
- desperatePrayer = 19236,
- divineHymn = 64843,
- divineStar = 110744,
- flashHeal = 2061,
- guardianSpirit = 47788,
- holyFire = 14914,
- holyNova = 132157,
- holyWordChastise = 88625,
- holyWordSanctify = 34861,
- holyWordSerenity = 2050,
- holyWordSalvation = 265202,
- heal = 2060,
- leapOfFaith = 73325,
- lightOfTuure = 33076,
- prayerOfHealing = 596,
- prayerOfMending = 33076,
- purify = 527,
- renew = 139,
- smite = 585,
- symbolOfHope = 64901,
- },
- artifacts = {
- -- lightOfTuure = 208065,
- },
- buffs = {
- angelicFeather = 121557,
- blessingOfTuure = 196578,
- divinity = 197031,
- echoOfLight = 77489,
- flashConcentration = 336267,
- powerOfTheNaaru = 196489,
- prayerOfMending = 41635,
- renew = 139,
- surgeOfLight = 114255,
- spiritOfRedemption = 27827,
- },
- debuffs = {
-
-
- },
- glyphs = {
-
- },
- runeforges = {
- flashConcentration = 336266,
- },
- talents = {
- angelicFeather = 121536,
- apotheosis = 200183,
- bodyAndSoul = 64129,
- --bodyAndMind = 214121,
- divineStar = 110744,
- --divinity = 197031,
- halo = 120517,
- --piety = 197034,
- shiningForce = 204263,
- surgeOfLight = 109186,
- --symbolOfHope = 64901,
- },
- },
- -- Shadow
- [258] = {
- abilities = {
- damnation = 341374,
- darkAscension = 280711,
- dispersion = 47585,
- devouringPlague = 335467,
- mindBlast = 8092,
- mindBomb = 205369,
- mindbender = 200174,
- mindFlay = 15407,
- mindSear = 48045,
- mindVision = 2096,
- powerInfusion = 10060,
- powerWordShield = 17,
- psychicHorror = 64044,
- psychicScream = 8122,
- purifyDisease = 213634,
- shadowMend = 186263,
- shadowWordVoid = 205351,
- shadowfiend = 34433,
- shadowform = 232698,
- silence = 15487,
- surrenderToMadness = 193223,
- vampiricEmbrace = 15286,
- vampiricTouch = 34914,
- voidBolt = 205448,
- devoidBolt = 343355,
- voidEruption = 228260,
- voidForm = 228264,
- },
- artifacts = {
- lashOfInsanity = 238137,
- massHysteria = 194378,
- sphereOfInsanity = 194179,
- toThePain = 193644,
- touchOfDarkness = 194007,
- unleashTheShadows = 194093,
- voidCorruption = 194016,
- voidTorrent = 205065,
- },
- buffs = {
- chorusOfInsanity = 279572,
- darkThoughts = 341207,
- dispersion = 47585,
- dissonantEchoes = 343144,
- harvestedThoughts = 288343,
- lingeringInsanity = 199849,
- powerWordShield = 17,
- powerInfusion = 10060,
- shadowyInsight = 124430,
- shadowform = 232698,
- surrenderedSoul = 212570,
- surrenderToMadness = 193223,
- thoughtsHarvester = 288340,
- unfurlingDarkness = 341282,
- void = 211657,
- voidForm = 194249,
- voidTorrent = 205065,
- zeksExterminatus = 236546, -- Legendary Cloak proc
- },
- debuffs = {
- devouringPlague = 335467,
- hungeringVoid = 345218,
- mindFlay = 15407,
- vampiricTouch = 34914,
- weakenedSoul = 6788,
- },
- glyphs = {
-
- },
- runeforges = {
- shadowflamePrism = 336143,
- },
- talents = {
- fortressOfTheMind = 193195,
- deathAndMadness = 321291,
- unfurlingDarkness = 341273,
- bodyAndSoul = 64129,
- sanlayn = 199855,
- twistOfFate = 109142,
- misery = 238558,
- searingNightmare = 341385,
- mindBomb = 205369,
- psychicHorror = 64044,
- auspiciousSpirits = 155271,
- psychicLink = 199484,
- shadowCrash = 205385,
- damnation = 341374,
- mindbender = 200174,
- voidTorrent = 263165,
- ancientMadness = 341240,
- hungeringVoid = 345218,
- surrenderToMadness = 319952,
- },
- traits = {
- chorusOfInsanity = 278661,
- deathThroes = 278659,
- searingDialogue = 272788,
- spitefulApparitions = 277682,
- thoughtHarvester = 288340,
- whispersOfTheDamned = 275722,
- },
- },
- -- All
- Shared = {
- abilities = {
- ascendedBlast = 325283,
- ascendedNova = 325020,
- boonOfTheAscended = 325013,
- desperatePrayer = 19236,
- dispelMagic = 528,
- fade = 586,
- faeGuardians = 327661,
- flashHeal = 2061,
- leapOfFaith = 73325,
- levitate = 1706,
- massDispel = 32375,
- massResurrection = 212036,
- mindBlast = 8092,
- mindControl = 605,
- mindgames = 323673,
- mindSoothe = 453,
- mindVision = 2096,
- powerInfusion = 10060,
- powerWordFortitude = 21562,
- powerWordShield = 17,
- psychicScream = 8122,
- resurrection = 2006,
- shadowWordDeath = 32379,
- shackleUndead = 9484,
- shadowWordPain = 589,
- smite = 585,
- },
- artifacts = {
-
- },
- buffs = {
- classHallSpeed = 224098,
- boonOfTheAscended = 325013,
- faeGuardians = 327661,
- levitate = 111759,
- powerWordFortitude = 21562,
- powerWordShield = 17,
-
- },
- conduits = {
- dissonantEchoes = 338342,
- },
- covenants = {
- boonOfTheAscended = 325013,
- ascendedBlast = 325315,
- ascendedEruption = 325326,
- ascendedNova = 325020,
- faeGuardians = 327661,
- mindgames = 323673,
- unholyNova = 324724,
- },
- debuffs = {
- shadowWordPain = 589,
- weakenedSoul = 6788,
- wrathfulFaerie = 327703,
-
- },
- glyphs = {
-
- },
- talents = {
-
- },
- },
- },
- ROGUE = {
- -- Assassination
- [259] = {
- abilities = {
- -- ambush = 8676,
- -- crimsonTempest = 121411,
- -- deadlyPoison = 315584,
- -- envenom = 32645,
- -- eviscerate = 196819,
- -- exsanguinate = 200806,
- -- fanOfKnives = 51723,
- -- garrote = 703,
- -- mutilate = 1329,
- -- poisonedKnife = 185565,
- -- rupture = 1943,
- -- shadowstep = 36554,
- -- sinisterStrike = 1752,
- -- vendetta = 79140,
- },
- artifacts = {
-
- },
- buffs = {
- blindside = 121153,
- deadlyPoison = 2823,
- elaboratePlanning = 193641,
- envenom = 32645,
- leechingPoison = 108211,
- sharpenedBlades = 272916,
- masterAssassin = 256735,
- subterfuge = 115192,
- theDreadlordsDeceit = 208692,
- },
- debuffs = {
- crimsonTempest = 121411,
- deadlyPoison = 2818,
- garrote = 703,
- internalBleeding = 154953,
- rupture = 1943,
- surgeOfToxins = 192425,
- toxicBlade = 245389,
- vendetta = 79140,
- },
- glyphs = {
-
- },
- talents = {
- blindside = 328085,
- crimsonTempest = 121411,
- elaboratePlanning = 193640,
- elusiveness = 79008,
- exsanguinate = 200806,
- hiddenBlades = 270061,
- internalBleeding = 154904,
- ironWire = 196861,
- leechingPoison = 280716,
- masterAssassin = 255989,
- masterPoisoner = 196864,
- nightstalker = 14062,
- poisonBomb = 255544,
- subterfuge = 108208,
- venomRush = 152152,
- },
- traits = {
- doubleDose = 273007,
- echoingBlades = 287649,
- sharpenedBlades = 272911,
- shroudedSuffocation = 278666,
- twistTheKnife = 273488,
- },
- runeforges = {
- dashingScoundrel = 340081,
- doomblade = 340082,
- zoldyckInsignia = 340083,
- duskwalkersPatch = 340084,
- },
- },
- -- Outlaw
- [260] = {
- abilities = {
- adrenalineRush = 13750,
- ambush = 8676,
- betweenTheEyes = 315341,
- bladeFlurry = 13877,
- bladeRush = 271877,
- blind = 2094,
- curseOfTheDreadblades = 202665,
- dispatch = 2098,
- ghostlyStrike = 196937,
- gouge = 1776,
- grapplingHook = 195457,
- killingSpree = 51690,
- masteryMainGauche = 76806,
- pistolShot = 185763,
- riposte = 199754,
- rollTheBones = 315508,
- sinisterStrike = 193315,
- dreadblades = 343142,
- },
- artifacts = {
- blackPowder = 216230,
- bladeDancer = 202507,
- bladeMaster = 202628,
- blunderbuss = 202897,
- blurredTime = 202769,
- cannonballBarrage = 185767,
- curseOfTheDreadblades = 202665,
- cursedEdge = 202463,
- cursedSteel = 214929,
- deception = 202755,
- fatesThirst = 202514,
- fatebringer = 202524,
- fortuneStrikes = 202530,
- fortunesBoon = 202907,
- fortunesStrike = 202521,
- ghostlyShell = 202533,
- greed = 202820,
- gunslinger = 202522,
- hiddenBlade = 202573,
- killingSpree = 51690,
- loadedDice = 238139,
- },
- buffs = {
- adrenalineRush = 13750,
- alacrity = 193538,
- bladeFlurry = 13877,
- blunderbuss = 202895,
- broadside = 193356,
- buriedTreasure = 199600,
- deadShot = 272940,
- grandMelee = 193358,
- greenskinsWaterloggedWristcuffs = 209420,
- hiddenBlade = 202754,
- jollyRoger = 199603,
- loadedDice = 256171,
- opportunity = 195627,
- ruthlessPrecision = 193357,
- rollTheBones = {
- broadside = 193356,
- buriedTreasure = 199600,
- grandMelee = 193358,
- ruthlessPrecision = 193357,
- skullAndCrossbones = 199603,
- trueBearing = 193359,
- },
- sharkInfestedWaters = 193357,
- skullAndCrossbones = 199603,
- snakeeeyes = 275863, --typo, leaving in not to break stuff
- snakeeyes = 275863,
- swordplay = 211669,
- trueBearing = 193359,
- wits = 288988,
- },
- debuffs = {
- ghostlyStrike = 196937,
- gouge = 1776,
- betweenTheEyes = 199804,
- preyOnTheWeak = 255909,
- blind = 2094,
- },
- glyphs = {
-
- },
- talents = {
- acrobaticStikes = 196924, -- typo, leaving in to not break old profiles
- acrobaticStrikes = 196924,
- dirtyTricks = 108216,
- bladeRush = 271877,
- ghostlyStrike = 196937,
- grapplingHook = 256188,
- hitAndRun = 196922,
- ironStomach = 193546,
- killingSpree = 51690,
- weaponmaster = 200733,
- quickDraw = 196938,
- blindingPowder = 256165,
- loadedDice = 256170,
- dancingSteel = 272026,
- dreadblades = 343142,
- },
- traits = {
- deadshot = 272935,
- aceupyoursleeve = 278676,
- snakeeyes = 275846,
- keepYourWitsAboutYou = 288979,
-
- },
- runeforges = {
- greenskinsWickers = 340085,
- guileCharm = 340086,
- celerity = 340087,
- concealedBlunderbuss = 340088,
- },
- conduits = {
- countTheOdds = 341546,
- },
- },
- -- Subtlety
- [261] = {
- abilities = {
- backstab = 53,
- blackPowder = 319175,
- rupture = 1943,
- shadowstrike = 185438,
- shurikenStorm = 197835,
- shurikenToss = 114014,
- symbolsOfDeath = 212283,
- },
- artifacts = {
-
- },
- buffs = {
- danseMacabre = 393969,
- deeperDaggers = 383405,
- finalityRupture = 385951,
- flagellation = 384631,
- flagellationPersist = 394758,
- lingeringShadow = 385960,
- masterAssassin = 256735,
- masterOfShadows = 196980,
- nightsVengeance = 273424,
- perforatedVeins = 341572,
- premeditation = 343160,
- shadowBlades = 121471,
- shadowDance = 185422,
- sharpenedBlades = 272916,
- shurikenCombo = 245640,
- shurikenTornado = 277925,
- subterfuge = 115192,
- symbolsOfDeath = 212283,
- symbolsOfDeathCrit = 227151,
- shotInTheDark = 257506,
- theDreadlordsDeceit = 228224,
- theRotten = 394203,
- },
- debuffs = {
- rupture = 1943,
- findWeakness = 91021,
- nightblade = 195452,
- shadowsGrasp = 206760,
- },
- glyphs = {
-
- },
- talents = {
- cloakedInShadows = 382515, --
- danseMacabre = 382528, --
- darkBrew = 382504, --
- darkShadow = 245687, --
- deepeningShadows = 185314, --
- deeperDaggers = 382517, --
- ephemeralBond = 426563, --
- exhilaratingExecution = 428486, --
- fadeToNothing = 382514, --
- finality = 382525, --
- findWeakness = 91023, --
- flagellation = 384631, --
- gloomblade = 200758, --
- goremawsBite = 426591, --
- improvedBackstab = 319949, --
- improvedShadowDance = 393972, --
- improvedShadowTechniques = 394023, --
- improvedShurikenStorm = 319951, --
- inevitability = 382512, --
- invigoratingShadowdust = 382523, --
- lingeringShadow = 382524, --
- masterOfShadows = 196976, --
- nightTerrors = 277953, --
- perforatedVeins = 382518, --
- plannedExecution = 382508, --
- premeditation = 343160, --
- quickDecisions = 382503, --
- relentlessStrikes = 58423, --
- replicatingShadows = 382506, --
- secretStratagem = 394320, --
- secretTechnique = 280719, --
- sepsis = 385408, --
- shadowBlades = 121471, --
- shadowFocus = 108209, --
- shadowcraft = 426594, --
- shadowedFinishers = 382511, --
- shotInTheDark = 257505, --
- shroudedInDarkness = 382507, --
- shurikenTornado = 277925, --
- silentStorm = 385722, --
- swiftDeath = 394309, --
- terrifyingPace = 428387, --
- theFirstDance = 382505, --
- theRotten = 382015, --
- veiltouched = 382017, --
- warningSigns = 426555, --
- weaponmaster = 193537, --
- withoutATrace = 382513, --
- },
- traits = {
- bladeInTheShadows = 275896,
- nightsVengeance = 273418,
- replicatingShadows = 286121,
- sharpenedBlades = 272911,
- theFirstDance = 278681,
- perforate = 277720,
- inevitability = 278683,
- },
- runeforges = {
- finality = 340089,
- akaarisSoulFragment = 340090,
- },
- conduits = {
- deeperDaggers = 341549,
- perforatedVeins = 341567,
- plannedExecution = 341556,
- stilettoStaccato = 341559,
- },
- },
- -- All
- Shared = {
- abilities = {
- ambush = 8676,
- cheapShot = 1833,
- crimsonVial = 185311,
- cripplingPoison = 3408,
- distract = 1725,
- eviscerate = 196819,
- feint = 1966,
- instantPoison = 315584,
- kick = 1766,
- kidneyShot = 408,
- pickLock = 1804,
- pickPocket = 921,
- sap = 6770,
- shadowDance = 185313,
- shroudOfConcealment = 114018,
- sinisterStrike = 1752,
- sliceAndDice = 315496,
- sprint = 2983,
- stealth = 1784,
- vanish = 1856,
- woundPoison = 8679,
- },
- artifacts = {
-
- },
- buffs = {
- chaosBane = 356043,
- cloakOfShadows = 31224,
- coldBlood = 213981,
- cripplingPoison = 3408,
- deathFromAbove = 152150,
- deathlyShadows = 341202,
- feint = 1966,
- flagellation = 345569,
- flagellationPersist = 394758,
- instantPoison = 315584,
- leadByExample = 342156,
- masterAssassinsMark = 340094,
- numbingPoison = 5761,
- sepsis = 328305,
- shroudOfConcealment = 114018,
- sliceAndDice = 315496,
- sprint = 2983,
- stealth = 1784,
- stealthSepsis = 347037,
- theRotten = 341134,
- tricksOfTheTrade = 57934,
- vanish = 1856,
- woundPoison = 8679,
- echoingReprimand = 323558,
- thistleTea = 381623,
- },
- conduits = {
- lashingScars = 341310,
- plannedExecution = 341556,
- reverberation = 341264,
- septicShock = 341309,
- },
- covenants = {
- echoingReprimand = 323547,
- flagellation = 323654,
- flagellationCleanse = 346975,
- sepsis = 328305,
- serratedBoneSpike = 328547,
- },
- debuffs = {
- cheapShot = 1833,
- cripplingPoison = 3408,
- flagellation = 323654,
- instantPoison = 315584,
- kidneyShot = 408,
- numbingPoison = 5761,
- sap = 6770,
- sepsis = 328305,
- serratedBoneSpike = 328547,
- serratedBoneSpikeDot = 324073,
- shiv = 319504,
- woundPoison = 8679,
- },
- glyphs = {
- glyphOfBlackout = 219693,
- glyphOfBurnout = 220279,
- glyphOfDisguise = 63268,
- glyphOfFlashBang = 219678,
- },
- talents = {
- acrobaticStrikes = 196924, --
- airbourneIrritant = 200733, --
- alacrity = 193539, --
- atrophicPoison = 381637, --
- blackjack = 379005, --
- blind = 2094, --
- cheatDeath = 31230, --
- cloakOfShadows = 31224, --
- coldBlood = 382245, --
- deadenedNerves = 231719, --
- deadlyPrecision = 381542, --
- deeperStratagem = 193531, --
- echoingReprimand = 385616, --
- elusiveness = 79008, --
- evasion = 5277, --
- featherfoot = 423683, --
- fleetFooted = 378813, --
- gouge = 1776, --
- gracefulGuile = 423647, --
- improvedAmbush = 381620, --
- improvedSprint = 231691, --
- improvedWoundPoison = 319066, --
- ironStomach = 193546, --
- lethality = 382238, --
- leechingPoison = 280716, --
- masterPoisoner = 378436, --
- nightstalker = 14062, --
- nimbleFingers = 378427, --
- numbingPoison = 5761, --
- recuperator = 378996, --
- resoundingClarity = 381622, --
- reverberation = 394332, --
- rushedSetup = 378803, --
- soothingDarkness = 393970, --
- shadowDance = 185313, --
- shadowrunner = 378807, --
- shadowstep = 36554, --
- shiv = 5938, --
- stillshroud = 423662, --
- subterfuge = 108208, --
- superiorMixture = 423701, --
- thistleTea = 381623, --
- tightSpender = 381621, --
- tricksOfTheTrade = 57934, --
- unbreakableStride = 400804, --
- vigor = 14983, --
- virulentPoisons = 381543, --
- },
- runeforges = {
- deathlyShadows = 340092,
- essenceOfBloodfang = 340079,
- invigoratingShadowdust = 340080,
- markOfTheMasterAssassin = 340076,
- obedience = 354703,
- tinyToxicBlade = 340078,
- theRotten = 340091,
- },
- },
- },
- SHAMAN = {
- -- Elemental
- [262] = {
- abilities = {
- earthquake = 61882,
- earthShock = 8042,
- eyeOfTheStorm = 157375,
- fireBlast = 57984,
- fireElemental = 198067,
- hardenSkin = 118337,
- immolate = 118297,
- lavaBeam = 114074,
- meteor = 117588,
- pulverize = 118345,
- windGust = 157331,
- },
- artifacts = {
- },
- conduits = {
- callOfFlame = 338303,
- },
- buffs = {
- ascendance = 114050,
- bloodlust = 2825,
- echoingShock = 320125,
- elementalFocus = 16164,
- elementalMastery = 16166,
- emberTotem = 210658,
- hardenSkin = 118337,
- heroism = 32182,
- iceFury = 210714,
- lavaSurge = 77762,
- masterOfTheElements = 260734,
- powerOfTheMaelstrom = 191877,
- resonanceTotem = 202192,
- stormKeeper = 191634,
- stormTotem = 210652,
- surgeOfPower = 285514,
- tailwindTotem = 210659,
- tectonicThunder = 286949,
- windGust = 263806,
- },
- debuffs = {
- flameShock = 188389,
- immolate = 118297,
- lightningRod = 197209,
- },
- glyphs = {
-
- },
- talents = {
- aftershock = 273221,
- ancestralGuidance = 108281,
- ascendance = 114050,
- earthenRage = 170374,
- echoingShock = 320125,
- echoOfTheElements = 333919,
- elementalBlast = 117014,
- iceFury = 210714,
- liquidMagmaTotem = 192222,
- masterOfTheElements = 16166,
- primalElementalist = 117013,
- staticDischarge = 342243,
- stormElemental = 192249,
- stormKeeper = 191634,
- surgeOfPower = 262303,
- unlimitedPower = 260895,
-
- },
- traits = {
- naturalHarmony = 278697,
- tectonicThunder = 286949,
- },
- },
- -- Enhancement
- [263] = {
- abilities = {
- tempest = 454015,
- feralLunge = 196884,
- --windfuryTotem = 8512,
- windstrike = 115356, --17364,
- },
- artifacts = {
-
- },
- buffs = {
- ascendance = 114051,
- ashenCatalyst = 390371,
- awakeningStorms = 462131,
- cracklingSurge = 224127,
- crashLightning = 187878,
- clCrashLightning = 333964,
- -- convergingStorms = 198300,
- doomWinds = 384352,
- earthenWeapon = 392375,
- feralSpirit = 333957,
- hailstorm = 334196,
- hotHand = 215785,
- icyEdge = 224126,
- legacyOfTheFrostWitch = 384451,
- maelstromOfElements = 394677,
- moltenWeapon = 224125,
- primordialWave = 327164,
- splinteredElements = 354648,
- tempest = 454015,
- -- stormkeeper = 320137,
- -- stormbringer = 201845,
- },
- debuffs = {
- -- doomWinds = 335904,
- lashingFlames = 334168,
- },
- glyphs = {
-
- },
- talents = {
- alphaWolf = 198434,
- ancestralWolfAffinity = 382197,
- ascendance = 114051,
- ashenCatalyst = 390370,
- convergingStorms = 384363,
- crashLightning = 187874,
- crashingStorms = 334308,
- deeplyRootedElements = 378270,
- doomWinds = 384352,
- elementalAssault = 210853,
- elementalBlast = 117014,
- elementalSpirits = 262624,
- elementalWeapons = 384355,
- feralSpirit = 51533,
- fireNova = 333974,
- forcefulWinds = 262647,
- hailstorm = 334195,
- hotHand = 201900,
- iceStrike = 342240,
- improvedMaelstromWeapon = 383303,
- lashingFlames = 334046,
- lavaLash = 60103,
- legacyOfTheFrostWitch = 384450,
- moltenAssault = 334033,
- overflowingMaelstrom = 384149,
- primalMaelstrom = 384405,
- primordialWave = 375982,
- ragingMaelstrom = 384143,
- refreshingWaters = 378211,
- splinteredElements = 382042,
- staticAccumulation = 384411,
- stormblast = 319930,
- stormflurry = 344357,
- stormstrike = 17364,
- stormsWrath = 392352,
- sundering = 197214,
- swirlingMaelstrom = 384359,
- tempest = 454009,
- tempestStrikes = 428071,
- thorimsInvocation = 384444,
- unrulyWinds = 390288,
- windfuryWeapon = 33757,
- witchDoctorsAncestry = 384447,
- },
- traits = {
- lightningConduit = 275389,
- naturalHarmony = 278697,
- primalPrimer = 272992,
- strengthOfTheEarth = 273461,
- },
- },
- -- Restoration
- [264] = {
- abilities = {
- ancestralVision = 212048,
- waterShield = 52127,
- healingRain = 73920,
- healingTideTotem = 108280,
- healingWave = 77472,
- manaTideTotem = 16191,
- purifySpirit = 77130,
- riptide = 61295,
- spiritLinkTotem = 98008,
- recallCloudburstTotem = 201764,
- },
- artifacts = {
- -- giftOfTheQueen = 207778,
- },
- buffs = {
- ascendance = 114052,
- cloudburstTotem = 157504,
- waterShield = 52127,
- healingRain = 73920,
- heavyRainfall = 338344,
- jonatsFocus = 210607,
- lavaSurge = 77762,
- riptide = 61295,
- tidalWaves = 53390,
- unleashLife = 73685,
- undulation = 216251,
- swirlingCurrents = 338340,
- },
- conduits = {
- heavyRainfall = 338343,
- },
- debuffs = {
- flameShock = 188389,
- },
- glyphs = {
- },
- talents = {
- ancestralVigor = 207401,
- ancestralProtectionTotem = 207399,
- ascendance = 114052,
- cloudburstTotem = 157153,
- deluge = 200076,
- downpour = 207778,
- earthenWallTotem = 198838,
- echoOfTheElements = 108283,
- flashFlood = 280614,
- highTide = 157154,
- sugeofEarth = 320746,
- torrent = 200072,
- undulation = 200071,
- unleashLife = 73685,
- wellspring = 197995,
- },
- },
- -- All
- Shared = {
- abilities = {
- ancestralSpirit = 2008,
- astralRecall = 556,
- bloodlust = 2825,
- earthbindTotem = 2484,
- farSight = 6196,
- flameShock = 188389,
- flametongueWeapon = 318038,
- ghostWolf = 2645,
- healingSurge = 8004,
- heroism = 32182,
- lightningBolt = 188196,
- lightningShield = 192106,
- primalStrike = 73899,
- waterWalking = 546,
- skyfury = 462854,
- },
- artifacts = {
-
- },
- buffs = {
- astralShift = 108271,
- earthShield = 974,
- ghostWolf = 2645,
- maelstromWeapon = 344179,
- lightningShield = 192106,
- skyfury = 462854,
- spiritwalkersGrace = 79206,
- waterWalking = 546,
- },
- conduits = {
- astralProtection = 337964,
- },
- covenants = {
- -- chainHarvest = 320674,
- -- faeTransfusion = 328923,
- -- primordialWave = 326059,
- -- vesperTotem = 324386,
- },
- debuffs = {
- flameShock = 188389,
- frostShock = 196840,
- hex = 51514,
- },
- glyphs = {
-
- },
- runeforges = {
- chainsOfDevastation = 336735,
- doomWinds = 335902,
- elementalEquilibrium = 336730,
- primalLavaActuators = 335895,
- echoesOfGreatSundering = 336215,
- seedsOfRampantGrowth = 356218,
- skybreakersFieryDemise = 336734,
- spiritwalkersTidalTotem = 335891,
- deeptremorStone = 336739,
- windspeakersLavaResurgence = 336063,
- deeplyRootedElements = 336738,
- },
- talents = {
- ascendingAir = 462791,
- ancestralGuidance = 108281,
- arcticSnowstorm = 462764,
- astralBulwark = 377933,
- astralShift = 108271,
- brimmingWithLife = 381689,
- callOfTheElements = 383011,
- capacitorTotem = 192058,
- chainHeal = 1064,
- chainLightning = 188443,
- cleanseSpirit = 51886,
- creationCore = 383012,
- earthElemental = 198103,
- earthgrabTotem = 51485,
- earthShield = 974,
- elementalOrbit = 383010,
- elementalResistance = 462368,
- elementalWarding = 381650,
- encasingCold = 462762,
- enhancedImbues = 462796,
- fireAndIce = 382886,
- flurry = 382888,
- frostShock = 196840,
- gracefulSpirit = 192088,
- greaterPurge = 378773,
- guardiansCudgel = 381819,
- gustOfWind = 192063,
- healingStreamTotem = 5394,
- hex = 51514,
- jetStream = 462817,
- lavaBurst = 51505,
- lightningLasso = 305483,
- manaSpring = 381930,
- naturesFury = 381655,
- naturesGuardian = 30884,
- naturesSwiftness = 378081,
- planesTraveler = 381647,
- poisonCleansingTotem = 383013,
- purge = 370,
- primordialBond = 381764,
- seasonedWinds = 355630,
- spiritWalk = 58875,
- spiritwalkersAegis = 378077,
- spiritwalkersGrace = 79206,
- spiritWolf = 260878,
- staticCharge = 265046,
- stoneBulwarkTotem = 108270,
- thunderousPaws = 378075,
- thundershock = 378779,
- thunderstorm = 51490,
- totemicFocus = 382201,
- totemicProjection = 108287,
- totemicRecall = 108285,
- totemicSurge = 381867,
- travelingStorms = 204403,
- tremorTotem = 8143,
- voodooMastery = 204268,
- windsOfAlakir = 382215,
- windRushTotem = 192077,
- windShear = 57994,
- windsOfAlAkir = 382215,
- },
- talentsHeroic = {
- -- Enhance / Elemental Stormbringer
- unlimitedPower = 454391,
- supercharge = 455110,
- voltaicSurge = 454919,
- awakeningStorms = 455129,
- stormcaller = 454021,
- stormSwell = 455088,
- arcDischarge = 455096,
- conductiveEnergy = 455123,
- shockingGrasp = 454022,
- rollingThunder = 454026,
- naturesProtection = 454027,
- surgingCurrents = 454372,
- -- Enhance / Restoration Totemic
- surgingTotem = 444995,
- totemicRebound = 445025,
- oversizedTotems = 445026,
- swiftRecall = 445027,
- imbuementMastery = 445028,
- whirlingElements = 445024,
- amplificationCore = 445029,
- oversurge = 445030,
- windBarrier = 445031,
- pulseCapacitor = 445032,
- supportiveImbuements = 445033,
- livelyTotems = 445034,
- reactivity = 445035,
- totemicCoordination = 445036,
- earthsurge = 455590,
- -- Elemental / Restoration Farseer
- callOfTheAncestors = 443450,
- latentWisdom = 443449,
- ancientFellowship = 443423,
- offeringFromBeyond = 443451,
- naturalHarmony = 443442,
- earthenCommunion = 443441,
- ancestralSwiftness = 443454,
- heedMyCall = 443444,
- routineCommunication = 443445,
- primordialCapacity = 443448,
- maelstromSupremacy = 443447,
- elementalReverb = 443418,
- spiritwalkersMomentum = 443425,
- finalCalling = 443446,
-
-
- },
- },
- },
- WARLOCK = {
- -- Affliction
- [265] = {
- abilities = {
- agony = 980,
- commanddemon = 119898,
- corruption = 172,
- darkSoul = 113860,
- deathbolt = 264106,
- demonicGateway = 311699,
- drainLife = 234153,
- drainSoul = 198590,
- felDomination = 333889,
- grimoireOfSacrifice = 108503,
- haunt = 48181,
- maleficRapture = 324536,
- mortalCoil = 6789,
- phantomSingularity = 205179,
- reapSouls = 216698,
- seedOfCorruption = 27243,
- shadowBolt = 232670,
- shadowBolt2 = 686,
- shadowLock = 171138,
- siphonLife = 63106,
- spellLock = 19647,
- spellLockgrimoire = 132409,
- soulEffigy = 205178,
- soulRot = 325640,
- soulSwap = 386951,
- soulTap = 387073,
- summonDarkglare = 205180,
- unstableAffliction = 30108,
- vileTaint = 278350,
- },
- artifacts = {
- },
- buffs = {
- cascadingCalamity = 275378,
- compoundingHorror = 199281,
- darkSoul = 113860,
- deadwindHarvester = 216708,
- demonicPower = 196099,
- empoweredLifeTap = 235156,
- felDomination = 333889,
- nightfall = 264571,
- tormentedSouls = 216695,
- wrathOfConsumption = 199646,
- inevitableDemise = 273525,
- },
- debuffs = {
- agony = 980,
- corruption = 146739,
- haunt = 48181,
- phantomSingularity = 205179,
- seedOfCorruption = 27243,
- siphonLife = 63106,
- shadowEmbrace = 32390,
- unstableAffliction = 316099,
- unstableAffliction2 = 233496,
- unstableAffliction3 = 233497,
- unstableAffliction4 = 233498,
- unstableAffliction5 = 233499,
- vileTaint = 278350,
- },
- glyphs = {
-
- },
- talents = {
- absoluteCorruption = 196103,
- creepingDeath = 264000,
- darkCaller = 334183,
- darkfury = 264874,
- seedOfCorruption = 196226,
- darkSoul = 113860,
- demonicSacrifice = 108503,
- drainSoul = 198590,
- grimoireOfSacrifice = 108503,
- haunt = 48181,
- howlOfTerror = 5484,
- inevitableDemise = 334319,
- nightfall = 108558,
- phantomSingularity = 205179,
- siphonLife = 63106,
- soulConduit = 215941,
- sowTheSeeds = 196226,
- vileTaint = 278350,
- writheInAgony = 196102,
- },
- traits = {
- cascadingCalamity = 275372,
- dreadfulCalling = 278727,
- inevitableDemise = 273521,
- pandemicInvocation = 289364,
- }
- },
- -- Demonology
- [266] = {
- abilities = {
- axeToss = 89766,
- bilescourgeBombers = 267211,
- callDreadstalkers = 104316,
- commandDemon = 119898,
- corruption = 172,
- demonbolt = 264178,
- demonicEmpowerment = 193396,
- demonicStrength = 267171,
- demonwrath = 193440,
- doom = 603,
- drainLife = 234153,
- felFirebolt = 104318,
- felstorm = 89751,
- grimoireFelguard = 111898,
- guillotine = 386833,
- handOfGuldan = 105174,
- implosion = 196277,
- netherPortal = 267217,
- powerSiphon = 264130,
- shadowBolt = 686,
- shadowflame = 205181,
- shadowLock = 171138,
- shadowsBite = 387322,
- spellLock = 19647,
- summonDarkglare = 205180,
- summonDemonicTyrant = 265187,
- summonVilefiend = 264119,
- thalkielsConsumption = 211714,
- },
- artifacts = {
- thalkielsAscendance = 238145,
- thalkielsConsumption = 211714,
- },
- buffs = {
- demonicCalling = 205146,
- demonicCore = 267102,
- demonicEmpowerment = 193396,
- demonicPower = 265273,
- demonwrath = 193440,
- explosivePotential = 275398,
- forbiddenKnowledge = 278738,
- netherPortal = 267218,
- shadowsBite = 272945,
- shadowyInspiration = 196269,
- },
- debuffs = {
- doom = 603,
- shadowflame = 205181,
- corruption = 172,
- },
- glyphs = {
-
- },
- talents = {
- bilescourgeBombers = 267211,
- darkfury = 264874,
- demonicStrength = 267171,
- demonicCalling = 205145,
- doom = 603,
- dreadlash = 264078,
- fromTheShadows = 267170,
- grimoireFelguard = 111898,
- howlOfTerror = 5484,
- innerDemons = 267216,
- netherPortal = 267217,
- powerSiphon = 264130,
- sacrificedSouls = 267214,
- summonVilefiend = 264119,
- soulConduit = 215941,
-
- },
- traits = {
- balefulInvocation = 287059,
- explosivePotential = 275395,
- shadowsBite = 272944,
- }
- },
- -- Destruction
- [267] = {
- abilities = {
- cataclysm = 152108,
- channelDemonfire = 196447,
- chaosBolt = 116858,
- conflagrate = 17962,
- corruption = 172,
- commandDemon = 119898,
- darkSoul = 113858,
- devourMagic = 19505,
- dimensionalRift = 196586,
- drainLife = 234153,
- felDomination = 333889,
- grimoireOfSacrifice = 108503,
- havoc = 80240,
- immolate = 348,
- incinerate = 29722,
- rainOfFire = 5740,
- shadowBolt = 686,
- shadowburn = 17877,
- shadowLock = 171138,
- singeMagic = 119905,
- singeMagicGrimoire = 132411,
- soulFire = 6353,
- spellLock = 19647,
- spellLockgrimoire = 132409,
- summonInfernal = 1122,
- },
- artifacts = {
-
- },
- buffs = {
- backdraft = 117828, --196406,
- crashingChaos = 277706,
- darkSoul = 113858,
- darkSoulInstability = 113858,
- demonicPower = 196099,
- empoweredLifeTap = 235156,
- felDomination = 333889,
- lessonsOfSpaceTime = 236174,
- lordOfFlames = 224103,
- },
- debuffs = {
- conflagrate = 265931,
- eradication = 196414,
- immolate = 157736,
- havoc = 80240,
- },
- glyphs = {
-
- },
- talents = {
- cataclysm = 152108,
- channelDemonfire = 196447,
- darkFury = 264874,
- darkSoul = 113858,
- darkSoulInstability = 113858,
- eradication = 196412,
- flashover = 267115,
- fireAndBrimstone = 196408,
- grimoireOfSacrifice = 108503,
- grimoireOfSupremacy = 266086,
- howlOfTerror = 5484,
- inferno = 270545,
- internalCombustion = 266134,
- reverseEntropy = 205148,
- roaringBlaze = 205184,
- shadowburn = 17877,
- soulFire = 6353,
- soulConduit = 215941,
- },
- traits = {
- crashingChaos = 277644
- }
- },
- -- Inital Warlock (1-10)
- [1454] = {
- abilities = {
- controlDemon = 93375,
- corruption = 172,
- curseOfWeakness = 702,
- drainLife = 234153,
- shadowBolt = 686,
- },
- buffs = {
-
- },
- debuffs = {
- corruption = 146739,
- curseOfWeakness = 702,
- drainLife = 234153,
- },
- },
- -- All
- Shared = {
- abilities = {
- amplifyCurse = 328774,
- banish = 710,
- burningRush = 111400,
- curseOfExhaustion = 334275,
- curseOfTongues = 1714,
- curseOfWeakness = 702,
- createHealthstone = 6201,
- createSoulwell = 29893,
- commandDemon = 119898,
- corruption = 172,
- darkPact = 108416,
- demonicGateway = 111771,
- demonicCircle = 48018,
- demonicTeleport = 48020,
- devourMagic = 19505,
- eyeOfKilrogg = 126,
- felDomination = 333889,
- fear = 5782,
- grimoireFelhunter = 111897,
- grimoireImp = 111859,
- grimoireOfService = 108501,
- grimoireSuccubus = 111896,
- grimoireVoidwalker = 111895,
- healthFunnel = 755,
- inquisitorsGaze = 386344,
- howlOfTerror = 5484,
- lifeTap = 1454,
- mortalCoil = 6789,
- ritualOfDoom = 342601,
- ritualOfSummoning = 698,
- shadowflame = 384069,
- shadowfury = 30283,
- shadowBulwark = 119907,
- spellLock = 19647,
- seduction = 6358,
- singeMagic = 89808,
- soulburn = 385899,
- soulHarvest = 196098,
- soulstone = 20707,
- summonSoulkeeper = 386256,
- --summonDoomguard = 18540,
- subjugateDemon = 1098,
- summonFelguard = 30146,
- summonFelhunter = 691,
- summonFelImp = 688,
- summonImp = 688,
- summonInfernal = 1122,
- summonSuccubus = 366222,
- summonVoidwalker = 697,
- summonWrathguard = 112870,
- unendingBreath = 5697,
- unendingResolve = 104773,
- unstableaffliction = 316099,
-
- },
- artifacts = {
-
- },
- buffs = {
- burningRush = 111400,
- demonicCircle = 48018,
- demonicSynergy = 171982,
- felDomination = 333889,
- healthFunnel = 755,
- grimoireOfSacrifice = 196099,
- sindoreiSpite = 208871,
- soulHarvest = 196098,
- soulstone = 20707,
- unendingBreath = 5697,
- unendingResolve = 104773,
- },
- conduits = {
- corruptingLeer = 339455,
-
- },
- covenants = {
- decimatingBolt = 325289,
- impendingCatastrophe = 321792,
- scouringTithe = 312321,
- },
- debuffs = {
- decimatingBolt = 325289,
- impendingCatastrophe = 321792,
- scouringTithe = 312321,
- soulRot = 325640,
- fear = 5782,
- curseOfExhaustion = 334275,
- curseOfTongues = 1714,
- curseOfWeakness = 702,
- },
- glyphs = {
- glyphOfTheFelImp = 219424,
- },
- talents = {
- burningRush = 111400,
- darkPact = 108416,
- demonSkin = 219272,
- mortalCoil = 6789,
-
- },
- },
- },
- WARRIOR = {
- -- Arms
- [71] = {
- abilities = {
- sweepingStrikes = 260708,
- },
- artifacts = {
-
- },
- buffs = {
- ashenJuggernaut = 335234,
- avatar = 389748,
- battlelord = 346369,
- battleStance = 386164,
- bladestorm = 446035,
- collateralDamage = 334783,
- colossusSmash = 208086,
- crushingAssault = 278826,
- deadlyCalm = 262228,
- defensiveStance = 386208,
- exploiter = 335452,
- imminentDemise = 445606,
- inForTheKill = 215550,
- juggernaut = 383290,
- mercilessBonegrinder = 383316,
- opportunist = 456120,
- overpower = 400805,
- recklessness = 389722,
- stoneHeart = 225947,
- suddenDeath = 52437,
- sweepingStrikes = 260708,
- testOfMight = 275540,
- },
- conduits = {
- ashenJuggernaut = 335232,
- },
- covenants = {
- condemn = 317349,
- condemnMassacre = 330334,
- },
- debuffs = {
- colossusSmash = 208086,
- deepWounds = 262115,
- executionersPrecision = 272870,
- markedForExecution = 445584,
- hamstring = 1715,
- ravager = 227876,
- rend = 772,
- },
- glyphs = {
- glyphOfThunderStrike = 68164,
- },
- talents = {
- armoredToTheTeeth = 384124,
- arterialBleed = 440995,
- avatar = 107574,
- barbaricTraining = 383082,
- battleStance = 386164,
- battlelord = 386630,
- blademastersTorment = 390138,
- bladestorm = 227847,
- bloodborne = 383287,
- bloodletting = 383154,
- bloodsurge = 384361,
- bluntInstruments = 383442,
- boneshaker = 429639,
- brutalFinish = 446085,
- cleave = 845,
- collateralDamage = 334779,
- colossalMight = 429634,
- colossusSmash = 167105,
- criticalThinking = 389306,
- crushingForce = 382764,
- cullingCyclone = 444778,
- danceOfDeath = 390713,
- -- deadlyCalm = 262228,
- deathDrive = 444770,
- deftExperience = 389308,
- demolish = 436358,
- dieByTheSword = 118038,
- dominanceOfTheColossus = 429636,
- dreadnaught = 262150,
- earthquaker = 440992,
- enduranceTraining = 382940,
- executionersPrecision = 386634,
- exhilaratingBlows = 383219,
- fatality = 383703,
- fervorOfBattle = 202316,
- fierceFollowthrough = 444773,
- finishingBlows = 400205,
- frothingBerserker = 392792,
- fueledByViolence = 383103,
- ignorePain = 190456,
- imminentDemise = 444769,
- impale = 383430,
- improvedExecute = 316405,
- improvedOverpower = 385571,
- improvedSweepingStrikes = 383155,
- inForTheKill = 248621,
- juggernaut = 383292,
- martialExpert = 429638,
- martialProwess = 316440,
- massacre = 281001,
- mercilessBonegrinder = 383317,
- mortalStrike = 12294,
- mountainOfMuscleAndScars = 429642,
- noStrangerToPain = 429644,
- oneAgainstMany = 429637,
- opportunist = 444774,
- overpower = 7384,
- overwhelmingBlades = 444772,
- practicedStrikes = 429647,
- preciseMight = 431548,
- reapTheStorm = 444775,
- relentlessPursuit = 444776,
- rend = 772,
- secondWind = 29838,
- sharpenedBlades = 383341,
- showNoMercy = 444771,
- skullsplitter = 260643,
- slayersDominance = 444767,
- slayersMalice = 444779,
- spitefulSerenity = 400314,
- stormOfSwords = 385512,
- stormWall = 388807,
- strengthOfArms = 400803,
- suddenDeath = 29725,
- tactician = 184783,
- testOfMight = 385008,
- tideOfBattle = 429641,
- twohandedWeaponSpecialization = 382896,
- unhinged = 386628,
- unrelentingOnslaught = 444780,
- valorInVictory = 383338,
- veteranVitality = 440993,
- viciousAgility = 444777,
- warMachine = 262231,
- warbreaker = 262161,
- warlordsTorment = 390140,
- },
- traits = {
- seismicWave = 277639,
- testOfMight = 275529,
- }
- },
- -- Fury
- [72] = {
- abilities = {
- crushingBlow = 335097,
- enrage = 184361,
- execute = 5308,
- whirlwind = 190411,
- },
- artifacts = {
-
- },
- buffs = {
- ashenJuggernaut = 392537,
- avatar = 107574,
- berserkerStance = 386196,
- bladestorm = 446035,
- bloodbath = 461288,
- brutalFinish = 446918,
- burstOfPower = 437121,
- crushingBlow = 396752,
- -- deathWish = 199261,
- enrage = 184362,
- enragedRegeneration = 184364,
- -- frenzy = 335082,
- meatCleaver = 85739,
- -- mercilessBonegrinder = 346574,
- opportunist = 456120,
- recklessness = 1719,
- suddenDeath = 280776,
- -- whirlwind = 85739,
- },
- conduits = {
-
- },
- covenants = {
- condemn = 317485,
- condemnMassacre = 330325,
- },
- debuffs = {
- championsMight = 386286,
- markedForExecution = 445584,
- odynsFuryTormentMh = 385060,
- },
- glyphs = {
-
- },
- talents = {
- armoredToTheTeeth = 384124,
- ashenJuggernaut = 392536,
- avatar = 107574,
- avatarOfTheStorm = 437134,
- barbaricTraining = 390674,
- berserkerStance = 386196,
- berserkersTorment = 390123,
- bladestorm = 227847,
- bloodborne = 385703,
- bloodcraze = 393950,
- bloodthirst = 23881,
- brutalFinish = 446085,
- burstOfPower = 437118,
- coldSteelHotBlood = 383959,
- crashingThunder = 436707,
- criticalThinking = 383297,
- cruelty = 392931,
- crushingForce = 382764,
- cullingCyclone = 444778,
- dancingBlades = 391683,
- deathDrive = 444770,
- deftExperience = 383295,
- depthsOfInsanity = 383922,
- -- dragonRoar = 118000,
- dualWieldSpecialization = 382900,
- enduranceTraining = 391997,
- enragedRegeneration = 184364,
- fierceFollowthrough = 444773,
- flashingSkies = 437079,
- focusInChaos = 383486,
- frenziedEnrage = 383848,
- frenzy = 335077,
- freshMeat = 215568,
- frothingBerserker = 215571,
- -- furiousCharge = 202224,
- gatheringClouds = 436201,
- groundCurrent = 436148,
- hackAndSlash = 383877,
- imminentDemise = 444769,
- improvedBloodthirst = 383852,
- improvedExecute = 316402,
- improvedRagingBlow = 383854,
- improvedWhirlwind = 12950,
- invigoratingFury = 383468,
- keepYourFeetOnTheGround = 438590,
- lightningStrikes = 434969,
- massacre = 206315,
- meatCleaver = 280392,
- odynsFury = 385059,
- onslaught = 315720,
- opportunist = 444774,
- overwhelmingBlades = 444772,
- powerfulEnrage = 440277,
- ragingBlow = 85288,
- rampage = 184367,
- reapTheStorm = 444775,
- recklessAbandon = 396749,
- recklessness = 1719,
- relentlessPursuit = 444776,
- -- seethe = 335091,
- showNoMercy = 444771,
- -- siegebreaker = 280772,
- singlemindedFury = 81099,
- slaughteringStrikes = 388004,
- slayersDominance = 444767,
- slayersMalice = 444779,
- snapInduction = 456270,
- steadfastAsThePeaks = 434970,
- stormBolts = 436162,
- stormOfSteel = 382953,
- stormShield = 438597,
- strengthOfTheMountain = 437068,
- suddenDeath = 280721,
- swiftStrikes = 383459,
- tenderize = 388933,
- thorimsMight = 436152,
- thunderBlast = 435607,
- titansTorment = 390135,
- titanicRage = 394329,
- unbridledFerocity = 389603,
- unhinged = 386628,
- unrelentingOnslaught = 444780,
- viciousAgility = 444777,
- viciousContempt = 383885,
- warMachine = 346002,
- warpaint = 208154,
- wrathAndFury = 392936,
- },
- traits = {
- coldSteelHotBlood = 288080
- }
- },
- -- Protection
- [73] = {
- abilities = {
- demoralizingShout = 1160,
- devastate = 20243,
- focusedRage = 204488,
- impendingVictory = 202168,
- intercept = 198304,
- lastStand = 12975,
- ravager = 228920,
- revenge = 6572,
- shieldBlock = 2565,
- shieldSlam = 23922,
- shieldWall = 871,
- shockwave = 46968,
- thunderClap = 6343,
- },
- artifacts = {
-
- },
- buffs = {
- avatar = 107574,
- defensiveStance = 71,
- lastStand = 12975,
- revenge = 5302,
- shieldBlock = 132404,
- shieldWall = 871,
- vengeanceFocusedRage = 202573,
- vengeanceIgnorePain = 202574,
- vengeanceRevenge = 202573,
- victorious = 32216,
- },
- conduits = {
-
- },
- covenants = {
- condemn = 317349,
- },
- debuffs = {
- deepwoundsProt = 115767,
- demoralizingShout = 1160,
- thunderClap = 6343,
- },
- glyphs = {
-
- },
- talents = {
- bestServedCold = 202560,
- warMachine = 316733,
- bolster = 280001,
- boomingVoice = 202743,
- cracklingThunder = 203201,
- devastator = 236279,
- dragonRoar = 118000,
- heavyRepercussions = 203177,
- indomitable = 202095,
- intoTheFray = 202603,
- menace = 275338,
- neverSurrender = 202561,
- punish = 275334,
- ravager = 228920,
- rumblingEarth = 275339,
- unstoppableForce = 275336,
- },
- },
- -- All
- Shared = {
- abilities = {
- attack = 88163,
- battleShout = 6673,
- berserkerRage = 18499,
- charge = 100,
- execute = 163201,
- hamstring = 1715,
- heroicThrow = 57755,
- pummel = 6552,
- shieldBlock = 2565,
- shieldSlam = 23922,
- slam = 1464,
- taunt = 355,
- victoryRush = 34428,
- whirlwind = 1680,
- },
- animas = {
- bloodstainedWhetstone = 322036,
- hurricaneHeart = 329122,
- championsDecree = 322054,
- periaptOfFuror = 322049,
- pleonexianCommand = 329165,
- rageMote = 322046,
- weatheredRunestone = 349916,
- singingStones = 329489,
- singingStonesOfIntimidation = 329484,
- singingStonesOfUnnerving = 329488,
- tarnishedMedallion = 348483,
- thunderingRoar = 322093,
- zovaalsWarbanner = 322027,
- bladedBulwark = 322026,
- fanOfLongswords = 322029,
- misshapenMirror = 329268,
- shriekingFlagon = 329275,
- voraciousCullingBlade = 329213,
- kyrianWarhelm = 314290,
- scratchedKnife = 322039,
- signetOfTormentedKings = 329345,
- smolderingInertia = 322032,
- umbralEarTrumpet = 329283,
- warlordsResolve = 322051,
- },
- artifacts = {
-
- },
- buffs = {
- battleShout = 6673,
- berserkerRage = 18499,
- firstStrike = 325381,
- fujiedasFury = 207776,
- ignorePain = 190456,
- outburst = 364010,
- seeingRed = 364006,
- smolderingInertia = 322032,
- spellReflection = 23920,
- victorious = 32216,
- },
- conduits = {
- viciousContempt = 337302,
- destructiveReverberations = 339939,
- },
- covenants = {
- ancientAftershock = 325886,
- conquerorsBanner = 324143,
- fleshcraft = 324631,
- spearOfBastion = 307865,
- },
- debuffs = {
- markOfFyralath = 414532,
- },
- glyphs = {
-
- },
- items = {
- treacherousTransmitter = 221023,
- },
- runeforges = {
- battlelord = 335274,
- cadenceOfFujieda = 335555,
- deathmaker = 335567,
- elysianMight = 357996,
- enduringBlow = 335458,
- exploiter = 335451,
- leaper = 335214,
- misshapenMirror = 335253,
- recklessDefence = 335582,
- reprisal = 335718,
- seismicReverberation = 335758,
- signetOfTormentedKings = 335266,
- sinfulSurge = 354131,
- theWall = 335239,
- thunderlord = 335229,
- unhinged = 335282,
- willOfTheBerserker = 335594,
- unbreakableWill = 335629,
- },
- talents = {
- angerManagement = 152278,
- berserkerShout = 384100,
- bitterImmunity = 383762,
- boundingStride = 202163,
- cacophonousRoar = 382954,
- championsMight = 386284,
- championsSpear = 376079,
- concussiveBlows = 383115,
- cracklingThunder = 203201,
- cruelStrikes = 392777,
- defensiveStance = 386208,
- -- demoralizingShout = 316464,
- doubleTime = 103827,
- fastFootwork = 382260,
- heroicLeap = 6544,
- honedReflexes = 391271,
- impendingVictory = 202168,
- intervene = 3411,
- intimidatingShout = 5246,
- leechingStrikes = 382258,
- menace = 275338,
- overwhelmingRage = 382767,
- painAndGain = 382549,
- piercingChallenge = 382948,
- piercingHowl = 12323,
- rallyingCry = 97462,
- ravager = 228920,
- reinforcedPlates = 382939,
- rumblingEarth = 275339,
- secondWind = 29838,
- seismicReverberation = 382956,
- shatteringThrow = 64382,
- -- shieldWall = 316834,
- shockwave = 46968,
- sidearm = 384404,
- spellReflection = 23920,
- stormBolt = 107570,
- thunderClap = 6343,
- thunderousRoar = 384318,
- thunderousWords = 384969,
- uproar = 391572,
- wildStrikes = 382946,
- wreckingThrow = 384110,
- },
- },
- },
- -- Global
- Shared = {
- Shared = {
- abilities = {
- autoAttack = 6603,
- autoShot = 75,
- giftOfTheNaaru = br.getRacial("Draenei"), --select(7, GetSpellInfo(GetSpellInfo(28880))),
- global = 61304,
- latentArcana = 296971,
- lightsJudgment = 255647,
- quakingPalm = 107079,
- racial = br.getRacial(),
- shadowmeld = 58984,
- },
- animas = {
- scrollOfElchaver = 305006,
- },
- artifacts = {
- artificialDamage = 226829,
- artificialStamina = 211309,
- concordanceOfTheLegionfall = 239042,
- },
- buffs = {
- ancientHysteria = 90355,
- battlePotionOfAgility = 279152,
- battlePotionOfIntellect = 279151,
- battlePotionOfStrength = 279153,
- superiorBattlePotionOfAgility = 298146,
- potionOfUnbridledFury = 300714,
- blessingOfSacrifice = 6940,
- greaterBlessingOfKings = 203538,
- greaterBlessingOfWisdom = 203539,
- battleScarredAugmentation = 270058, -- BfA Augment Rune Buff
- blessingOfFreedom = 1044,
- blessingOfProtection = 1022,
- bloodLust = {
- ancientHysteria = 90355,
- bloodlust = 2825,
- drumsOfRage = 146555,
- drumsOfTheMaelstrom = 256740,
- drumsOfTheMountain = 230935,
- heroism = 32182,
- netherwinds = 160452,
- primalRage = 264667,
- timewarp = 80353,
- },
- concordanceOfTheLegionfall = 239042,
- defiledAugmentation = 224001, -- Lightforged Augment Rune buff
- felFocus = 242551,
- flaskOfTenThousandScars = 188035,
- flaskOfTheCountlessArmies = 188034,
- flaskOfTheSeventhDemon = 188033,
- flaskOfTheWhisperedPact = 188031,
- flaskOfTheCurrents = 251836,
- flaskOfEndlessFathoms = 251837,
- flaskOfTheVastHorizon = 251838,
- flaskOfTheUndertow = 251839,
- flayedWingToxin = 345546,
- fruitfulMachinatins = 242623, -- Absorb Shield from Deceiver's Grand Design
- mistcallerOcarina = 330067, -- SL Trinket with group buff
- greaterFlaskOfEndlessFathoms = 298837,
- greaterFlaskOfTheCurrents = 298836,
- greaterFlaskOfTheUndertow = 298841,
- greaterFlaskOfTheVastHorizon = 298839,
- guidingHand = 242622, -- from The Deceiver's Grand Design
- heroism = 32182,
- ineffableTruth = 316801,
- latentArcana = 296962,
- netherwinds = 160452,
- norgannonsForesight = 236373,
- overchargeMana = 299624,
- potionOfBurstingBlood = 265514,
- potionOfFocusedResolve = 298317,
- prolongedPower = 229206,
- racial = br.getRacial(),
- razorCoral = 303570, -- Crit Buff from Ashvane's Razor Coral
- sephuz1 = 208051, -- the fulltime 10% movement, 2% haste buff
- sephuz2 = 208052, -- the proc, 70% movement, 25% haste buff
- sephuzCooldown = 226262, -- CD (30 seconds) for the proc
- shadowmeld = 58984,
- symbolOfHope = 64901,
- timeWarp = 80353,
- whispersOfInsanity = 176151,
- cracklingTourmaline = 290372,
- saphireofBrilliance = 290365,
- vigorEngaged = 287916,
- soulshape = 310143,
- soulIgnition = 345251,
- spectralFlaskOfPower = 307185,
- elementalPotionOfPower = 371024,
- elementalPotionOfUltimatePower = 371028,
- phialOfGlacialFury = 373257,
- phialOfTepidVersatility = 371172,
- icedPhialOfCorruptingRage = 374000,
- domineeringArrogance = 411661,
+ -- Get addon directory path
+ local sep = IsMacClient() and "/" or "\\"
+ local function getFilesLocation()
+ local wowDir = br._G.GetWoWDirectory() or ""
+ if wowDir:match('_retail_') then
+ return wowDir .. sep .. 'Interface' .. sep .. 'AddOns' .. sep .. br.loader.addonName
+ end
+ return wowDir .. sep .. br.loader.addonName
+ end
- },
- --TODO Add to API
- itemEnchantments = {
- --Dragonflight weapon Imbue's from inscription enchantments
- --Quality of aura should always be GOLD,SILVER,COPPER
- buzzingRune = { 6514, 6513, 6512 },
- chirpingRune = { 6695, 6694, 6515 },
- howlingRune = { 6518, 6517, 6516 },
- hissingRune = { 6839, 6837, 6838 },
- },
- conduits = {
+ local loadSpellList = function(label)
+ local spellData = br._G.ReadFile(getFilesLocation() .. sep .. "System" .. sep .. "Lists" .. sep .. "Expansions" .. sep .. label .. sep .. "Spells.lua")
+ if spellData then
+ local func = assert(loadstring(spellData, label .. " Spells"))
+ setfenv(func, setmetatable({}, {__index = _G}))
+ func(nil, br) -- Pass br as second argument to match "local _, br = ..."
+ else
+ br._G.print("|cffFF0000Error:|r " .. label .. " spell list not found!")
+ end
+ end
- },
- covenants = {
- covenantAbility = 313347,
- doorOfShadows = 300728,
- fleshcraft = 324631,
- soulshape = 310143,
- flicker = 324701,
- summonSteward = 324739,
- },
- debuffs = {
- bloodOfTheEnemy = 297108,
- concentratedFlame = 295368,
- conductiveInk = 302565,
- dampening = 110310,
- eyeOfLeotheras = 206649,
- necroticWound = 209858,
- razorCoral = 303568, --304877,
- repeatPerformance = 304409,
- shiverVenom = 301624,
- temptation = 234143,
- eyeOfCorruption = 315161,
- grandDelusions = 319695,
- graspingTendrils = 315176,
- creepingMadness = 313255,
- fixate = 318078,
- thirstForBlood = 266107,
- mightyBash = 5211,
- defiledGround = 314565,
- vileCorruption = 314392,
- timeToFeed = 162415, -- Oshir Iron Docks
- cascadingTerror = 314478,
- },
- essences = {
- },
- },
- },
-}
+ -- Load the appropriate spell list based on expansion
+ if br.isRetail then
+ -- Load Retail spell lists when created
+ br._G.print("Loading Retail spell lists...")
+ loadSpellList("Retail")
+ elseif br.isMOP then
+ -- Load MoP Classic spell lists
+ br._G.print("Loading MoP spell lists...")
+ loadSpellList("MOP")
+ elseif br.isCata then
+ br._G.print("Loading Cataclysm spell lists...")
+ -- Future: Load Cata spell lists
+ br._G.print("|cffFFFF00Warning:|r Cataclysm spell lists not yet implemented!")
+ elseif br.isWrath then
+ br._G.print("Loading Wrath spell lists...")
+ -- Future: Load Wrath spell lists
+ br._G.print("|cffFFFF00Warning:|r Wrath spell lists not yet implemented!")
+ elseif br.isBC then
+ -- Load Burning Crusade spell lists
+ br._G.print("Loading Burning Crusade spell lists...")
+ loadSpellList("TBC")
+ elseif br.isClassic then
+ -- Load Classic spell lists
+ br._G.print("Loading Classic spell lists...")
+ loadSpellList("Classic")
+ else
+ -- Default to MoP for backward compatibility
+ br._G.print("Unknown expansion, no spell lists loaded!")
+ -- local mopSpells = br._G.ReadFile(getFilesLocation() .. sep .. "System" .. sep .. "Lists" .. sep .. "Expansions" .. sep .. "MOP" .. sep .. "Spells.lua")
+ -- if mopSpells then
+ -- local func = assert(loadstring(mopSpells, "MoP Spells (default)"))
+ -- setfenv(func, setmetatable({}, {__index = _G}))
+ -- func(nil, br) -- Pass br as second argument to match "local _, br = ..."
+ -- end
+ end
+end
diff --git a/System/Loader/cBuilder.lua b/System/Loader/cBuilder.lua
index f563b6320b..ebf015ce9c 100644
--- a/System/Loader/cBuilder.lua
+++ b/System/Loader/cBuilder.lua
@@ -1,5 +1,7 @@
local _, br = ...
-br.loader = {}
+br.loader.cBuilder = br.loader.cBuilder or {}
+local cBuilder = br.loader.cBuilder
+
local sep = IsMacClient() and "/" or "\\"
local class = select(2, br._G.UnitClass('player'))
local function getFolderClassName(class)
@@ -9,16 +11,18 @@ local function getFolderClassName(class)
return formatClass
end
local function getFolderSpecName(class, specID)
+ if br.isClassic or br.isBC then return class end
for k, v in pairs(br.lists.spec[class]) do
if v == specID then return tostring(k) end
end
+ return "Initial"
end
local function getFilesLocation()
local wowDir = br._G.GetWoWDirectory() or ""
if wowDir:match('_retail_') then
- return wowDir .. sep .. 'Interface' .. sep .. 'AddOns' .. sep .. br.addonName
+ return wowDir .. sep .. 'Interface' .. sep .. 'AddOns' .. sep .. br.loader.addonName
end
- return wowDir .. sep .. br.addonName
+ return wowDir .. sep .. br.loader.addonName
end
local function errorhandler(err)
@@ -32,48 +36,128 @@ local function loadFile(profile, file, support)
print("Error: " .. file .. " Cannot Load. Please inform devs!")
print("Load Error Message: " .. tostring(errorMessage))
errorhandler(errorMessage)
+ return false
end
br._G.setfenv(func, custom_env)
- local success, xerrorMessage = xpcall(func, errorhandler);
+
+ -- Capture the actual error in the error handler
+ local actualError = nil
+ local function captureError(err)
+ actualError = err
+ return errorhandler(err)
+ end
+
+ local success = xpcall(func, captureError);
if not success then
print('Error: ' .. file .. ' Cannot Run. Please inform devs!')
- print("Run Error Message: " .. tostring(xerrorMessage))
- errorhandler(xerrorMessage)
+ print("Run Error Message: " .. tostring(actualError))
+ return false
end
+ return true
end
-- Load Rotation Files
-function br.loader.loadProfiles()
+function cBuilder:loadProfiles()
-- Search each Profile in the Spec Folder
- br._G.wipe(br.rotations)
- local specID = br._G.GetSpecializationInfo(br._G.GetSpecialization())
+ br._G.wipe(br.loader.rotations)
+ local specID = br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization())
local IDLength = math.floor(math.log10(specID) + 1)
local folderSpec = getFolderSpecName(class, specID)
local path = getFilesLocation() .. sep .. 'Rotations' .. sep .. getFolderClassName(class) .. sep .. folderSpec .. sep
+
-- br._G.print("Path: " .. tostring(path))
- local profiles = br._G.GetDirectoryFiles(path)
+ local profiles = br._G.GetDirectoryFiles(path) or {}
+ local matchedProfiles = 0
-- br._G.print("Profiles: " .. tostring(#profiles))
for k, file in pairs(profiles) do
-- br._G.print("Path: " .. path .. ", File: " .. file)
local profile = br._G.ReadFile(path .. file) or ""
-- br._G.print("Profile: " .. tostring(profile))
+
+ -- Extract spec ID
local start = string.find(profile, "local id = ", 1, true) or 0
local stringEnd = start + IDLength + 10
local profileID = math.floor(tonumber(string.sub(profile, start + 10, stringEnd)) or 0)
- -- br._G.print("ProfileID: " .. tostring(profileID) .. ", SpecID: " .. tostring(specID))
- if profileID == specID then
- br._G.print("Loading Profile")
+
+ -- Extract expansion identifier (optional - defaults to br.isMOP for backward compatibility)
+ local expansionStart = string.find(profile, 'local expansion = ', 1, true)
+ local profileExpansion = nil
+ if expansionStart then
+ -- Extract the value after "local expansion = " (could be br.isMOP, br.isRetail, etc.)
+ local valueStart = expansionStart + 18
+ local valueEnd = string.find(profile, '\n', valueStart) or string.find(profile, '\r', valueStart)
+ if valueEnd then
+ local expansionValue = string.sub(profile, valueStart, valueEnd - 1)
+ -- Remove any comments
+ local commentPos = string.find(expansionValue, '--', 1, true)
+ if commentPos then
+ expansionValue = string.sub(expansionValue, 1, commentPos - 1)
+ end
+ expansionValue = expansionValue:match("^%s*(.-)%s*$") -- trim whitespace
+
+ -- Evaluate the expression (e.g., "br.isMOP" becomes true/false)
+ local func = loadstring("return " .. expansionValue)
+ if func then
+ -- Set the environment so the function has access to br
+ setfenv(func, setmetatable({br = br}, {__index = _G}))
+ profileExpansion = func()
+ end
+ end
+ end
+
+ -- Extract rotation name from profile content
+ local profileName = file -- Default to filename
+ local nameStart = string.find(profile, 'local rotationName = "', 1, true)
+ if nameStart then
+ local valueStart = nameStart + 22 -- Length of 'local rotationName = "'
+ local valueEnd = string.find(profile, '"', valueStart)
+ if valueEnd then
+ profileName = string.sub(profile, valueStart, valueEnd - 1)
+ end
+ else
+ -- Try single quotes if double quotes not found
+ nameStart = string.find(profile, "local rotationName = '", 1, true)
+ if nameStart then
+ local valueStart = nameStart + 22
+ local valueEnd = string.find(profile, "'", valueStart)
+ if valueEnd then
+ profileName = string.sub(profile, valueStart, valueEnd - 1)
+ end
+ end
+ end
+
+ -- br._G.print("ProfileID: " .. tostring(profileID) .. ", SpecID: " .. tostring(specID) .. ", Expansion: " .. tostring(profileExpansion))
+
+ -- Load only if BOTH spec ID matches and expansion is true
+ if profileID == specID and profileExpansion then
+ br._G.print("Loading Profile: " .. profileName .. " for expansion: " .. br.api.expansionName)
loadFile(profile, file, false)
+ matchedProfiles = matchedProfiles + 1
end
end
+
+ -- Warn if no profiles matched for this expansion
+ if matchedProfiles == 0 and #profiles > 0 then
+ br._G.print("|cffFF0000BadRotations:|r Found " .. #profiles .. " profile(s) but none matched for " .. br.api.expansionName .. " expansion!")
+ br._G.print("|cffFFFF00Create a Retail rotation or change profile expansion markers.|r")
+ end
+
+ -- Track per-spec no-rotation state so callers can avoid retrying
+ br.loader.noRotationsFound = br.loader.noRotationsFound or {}
+ if matchedProfiles == 0 then
+ br.loader.noRotationsFound[specID] = true
+ else
+ br.loader.noRotationsFound[specID] = nil
+ end
end
-- Load Support Files - Specified in Rotation File
-function br.loadSupport(thisFile) -- Loads support rotation file from Class Folder
+function cBuilder:loadSupport(thisFile)
+ -- Loads support rotation file from Class Folder
if thisFile == nil then return end
- br.rotations.support = br.rotations.support or {}
- if br.rotations.support[thisFile] then
- br._G.wipe(br.rotations.support[thisFile])
+ br.loader.rotations.support = br.loader.rotations.support or {}
+ if br.loader.rotations.support[thisFile] then
+ br._G.wipe(br.loader.rotations.support[thisFile])
end
local file = thisFile .. ".lua"
local profile = br._G.ReadFile(getFilesLocation() ..
@@ -83,33 +167,36 @@ end
-- Generate Profile API
local loadRotations = false
-function br.loader:new(spec, specName)
+function cBuilder:new(spec, specName)
local loadStart = br._G.debugprofilestop()
- local self = br.cCharacter:new(tostring(select(1, br._G.UnitClass("player"))))
+ local self = br.loader.cCharacter:new()--tostring(select(1, br._G.UnitClass("player"))))
-- local player = "player" -- if someone forgets ""
if specName == nil then specName = "Initial" end
self.profile = specName
-- Mandatory !
- if br.rotations[spec] == nil then
- br.loader.loadProfiles()
+ if br.loader.rotations[spec] == nil then
+ cBuilder:loadProfiles()
br.rotationChanged = true
end
if not loadRotations then
- if br.selectedProfile ~= nil and br.rotations[spec] ~= nil and br.rotations[spec][br.selectedProfile] then
- br._G.print("Selecting Previous Rotation: " .. br.rotations[spec][br.selectedProfile].name)
- self.rotation = br.rotations[spec][br.selectedProfile]
+ if br.loader.selectedProfile ~= nil and br.loader.rotations[spec] ~= nil and br.loader.rotations[spec][br.loader.selectedProfile] then
+ -- br._G.print("Selecting Previous Rotation: " .. br.loader.rotations[spec][br.loader.selectedProfile].name)
+ self.rotation = br.loader.rotations[spec][br.loader.selectedProfile]
loadRotations = true
- elseif br.rotations[spec] ~= nil then
- br._G.print("No Previously Selected Rotation, Defaulting To: " .. br.rotations[spec][1].name)
- self.rotation = br.rotations[spec][1]
+ elseif br.loader.rotations[spec] ~= nil then
+ br._G.print("No Previously Selected Rotation, Defaulting To: " .. br.loader.rotations[spec][1].name)
+ self.rotation = br.loader.rotations[spec][1]
loadRotations = true
else
if not loadRotations then
br._G.print("No Rotations Found!")
self.rotation = {}
+ -- Mark that this spec currently has no rotations to avoid repeated load attempts
+ br.loader.noRotationsFound = br.loader.noRotationsFound or {}
+ br.loader.noRotationsFound[spec] = true
end
return
end
@@ -126,12 +213,16 @@ function br.loader:new(spec, specName)
local sharedClassSpells = br.lists.spells[playerClass]["Shared"]
local sharedGlobalSpells = br.lists.spells["Shared"]["Shared"]
-- Get the new spells
- local function getSpells(spellTable)
+ -- origin: "global" | "sharedClass" | "spec"
+ local function getSpells(spellTable, origin)
self.spells = self.spells or {}
+ -- track where each spell entry came from so we can prefer spec-specific entries
+ if self.spellSource == nil then self.spellSource = {} end
-- Look through spell type subtables
for spellType, spellTypeTable in pairs(spellTable) do
-- Create spell type subtable in br.player.spells if not already there.
self.spells[spellType] = self.spells[spellType] or {}
+ self.spellSource[spellType] = self.spellSource[spellType] or {}
-- Look through spells for spell type
for spellRef, spellID in pairs(spellTypeTable) do
-- If spellType is items then add to br.player.spells.items
@@ -139,15 +230,56 @@ function br.loader:new(spec, specName)
self.items = self.items or {}
self.items[spellRef] = spellID
else
+ -- Check if br.api.wow.GetSpellInfo returns a valid spell name
+ local name, _, _, _, _, _, gsiSpellID = br.api.wow.GetSpellInfo(spellID)
+ if spellID == 1449 then
+ print("Debug: Checking spellID 1449 (Name: "..(name or "nil")..", GSI ID: "..(gsiSpellID or "nil")..")")
+ end
+ if type(spellID) ~= "table" then
+ if (not name or gsiSpellID ~= spellID) then
+ -- br._G.print("SpellID not valid: " .. tostring(spellID))
+ br._G.print("|cffff0000"..spellType..": |r" ..
+ spellRef ..
+ "|cffff0000 with ID: |r" ..
+ spellID .. "|cffff0000 is not valid in the list of "..spellType.." Spell List.")
+ end
+ else
+ -- If spellID is a table, then it is a list of spellIDs, so we need to check each one
+ for spellRef, id in pairs(spellID) do
+ local name, _, _, _, _, _, gsiSpellID = br.api.wow.GetSpellInfo(id)
+ if (not name or gsiSpellID ~= id) then
+ -- br._G.print("SpellID not valid: " .. tostring(id))
+ br._G.print("|cffff0000"..spellType..": |r" ..
+ spellRef ..
+ "|cffff0000 with ID: |r" ..
+ id .. "|cffff0000 is not valid in the list of "..spellType.." Spell List.")
+ end
+ end
+ end
-- Assign spell to br.player.spells for the spell type
- self.spells[spellType][spellRef] = spellID
+ -- If a spec-specific entry already exists, prefer it (don't overwrite with Shared/global)
+ local existingSource = self.spellSource[spellType][spellRef]
+ if existingSource == "spec" and origin ~= "spec" then
+ -- keep existing spec-specific entry
+ else
+ self.spells[spellType][spellRef] = spellID
+ self.spellSource[spellType][spellRef] = origin or "shared"
+ end
-- Assign active spells to Abilities Subtable and base br.player.spells
if (spellType == 'abilities' or spellType == 'covenants' or ((spellType == 'traits' or spellType == 'talents' or spellType == 'talentsHeroic') and spec < 1400))
and type(spellID) ~= 'table' and not br._G.C_Spell.IsSpellPassive(spellID)
then
self.spells.abilities = self.spells.abilities or {}
- self.spells.abilities[spellRef] = spellID
- self.spells[spellRef] = spellID
+ -- When mapping into abilities/base table, apply same origin-preference rules
+ local existingAbilitiesSource = self.spellSource["abilities"] and self.spellSource["abilities"][spellRef]
+ if existingAbilitiesSource == "spec" and origin ~= "spec" then
+ -- keep existing spec-specific ability
+ else
+ self.spells.abilities[spellRef] = spellID
+ self.spells[spellRef] = spellID
+ self.spellSource["abilities"] = self.spellSource["abilities"] or {}
+ self.spellSource["abilities"][spellRef] = origin or "shared"
+ end
end
end
end
@@ -158,130 +290,91 @@ function br.loader:new(spec, specName)
-- Talent Test
-- getTalentTest()
-- Shared Global Spells
- getSpells(sharedGlobalSpells)
+ getSpells(sharedGlobalSpells, "global")
-- Shared Class Spells
- getSpells(sharedClassSpells)
+ getSpells(sharedClassSpells, "sharedClass")
-- Spec Spells - Don't Load on Initial Levels
- if br.lists.spells[playerClass][spec] ~= nil then getSpells(specSpells) end
+ if br.lists.spells[playerClass][spec] ~= nil then getSpells(specSpells, "spec") end
-- Ending the Race War!
if self.spells.abilities["racial"] == nil then
- local racialID = br.getRacial()
+ local racialID = self:getRacial()
self.spells.abilities["racial"] = racialID
self.spells.buffs["racial"] = racialID
self.spells["racial"] = racialID
end
end
- br.allTalents = {}
- -- Get Active Talents
- local function getActiveTalents(node, configId)
- local activeTalents = {}
- for _, entryID in pairs(node.entryIDsWithCommittedRanks) do
- local entryInfo = br._G.C_Traits.GetEntryInfo(configId, entryID)
- if entryInfo and entryInfo.definitionID then
- local definitionInfo = br._G.C_Traits.GetDefinitionInfo(entryInfo.definitionID)
- if definitionInfo.spellID ~= nil then
- activeTalents[definitionInfo.spellID] = activeTalents[definitionInfo.spellID] or {}
- activeTalents[definitionInfo.spellID].active = true
- activeTalents[definitionInfo.spellID].rank = node.activeRank or 0
- end
- end
- end
- return activeTalents
- end
-
- -- Check Existing Talents
- local function CheckExistingTalents(talentID)
- local playerClass = select(2, br._G.UnitClass('player'))
- if br.lists.spells[playerClass]["Shared"] == nil or br.lists.spells[playerClass][spec] == nil then
- return
- end
-
- local heroicTalents = br.lists.spells[playerClass]["Shared"]["talentsHeroic"] or {}
- local sharedTalents = br.lists.spells[playerClass]["Shared"]["talents"]
- local specTalents = br.lists.spells[playerClass][spec]["talents"]
-
- -- Helper function to find talent name by ID in a talent table
- local function findTalentNameByID(talentTable, searchID)
- for name, id in pairs(talentTable) do
- if id == searchID then
- return name
- end
- end
- return nil
- end
-
- -- Helper function to check if talent ID exists in any talent table
- local function talentIDExists(searchID)
- return findTalentNameByID(specTalents, searchID) ~= nil or
- findTalentNameByID(sharedTalents, searchID) ~= nil or
- findTalentNameByID(heroicTalents, searchID) ~= nil
- end
-
- -- Get the localized talent name for display purposes only
- local talentName = br._G.GetSpellInfo(talentID) or ("Unknown Spell " .. talentID)
-
- -- Check for missing entries
- if not talentIDExists(talentID) then
- br._G.print("|cffff0000Talent: |r" ..
- talentName ..
- "|cffff0000 with ID: |r" ..
- talentID .. " is not listed in the list of Talents Spell List.")
- end
- end
+ -- Update Talent Info
+ local function getTalentInfo()
+ -- Use expansion-specific talent system
+ local allTalents = br.api.wow.getTalentInfo(spec, self.spells.talents)
+ if self.talent == nil then self.talent = {} end
- -- Get All Talents
- local function getAllTalents()
- -- br.talentInfo = {}
- local talents = {}
- local configId = br._G.C_ClassTalents.GetActiveConfigID()
- if not configId then return talents end
- local configInfo = br._G.C_Traits.GetConfigInfo(configId)
- if not configInfo then return talents end
- for _, treeId in pairs(configInfo.treeIDs) do
- local nodes = br._G.C_Traits.GetTreeNodes(treeId)
- for _, nodeId in pairs(nodes) do
- local node = br._G.C_Traits.GetNodeInfo(configId, nodeId)
- local activeTalents = getActiveTalents(node, configId)
- for _, entryID in pairs(node.entryIDs) do
- local entryInfo = br._G.C_Traits.GetEntryInfo(configId, entryID)
- if entryInfo and entryInfo.definitionID then
- local definitionInfo = br._G.C_Traits.GetDefinitionInfo(entryInfo.definitionID)
- local talentID = definitionInfo.spellID
- if talentID ~= nil then
- talents[talentID] = talents[talentID] or {}
- talents[talentID].active = activeTalents[talentID] and true or false
- talents[talentID].rank = node.activeRank or 0
- -- Check existing talents
- CheckExistingTalents(talentID)
+ -- Check for active talents not in spell lists (only for actual specs, not Initial profiles)
+ if spec < 1400 and br.lists and br.lists.spells then
+ local playerClass = select(2, br._G.UnitClass('player'))
+ if br.lists.spells[playerClass] and br.lists.spells[playerClass]["Shared"] and br.lists.spells[playerClass][spec] then
+ local heroicTalents = br.lists.spells[playerClass]["Shared"]["talentsHeroic"] or {}
+ local sharedTalents = br.lists.spells[playerClass]["Shared"]["talents"] or {}
+ local specTalents = br.lists.spells[playerClass][spec]["talents"] or {}
+
+ -- Check each active talent from the game
+ for talentID, talentData in pairs(allTalents) do
+ if talentData.active then
+ local spellName = br.api.wow.GetSpellInfo(talentID)
+ if spellName then
+ local talentName = br.functions.misc:convertName(spellName)
+
+ -- Check if talent exists in any spell list
+ if sharedTalents[talentName] == nil and specTalents[talentName] == nil and heroicTalents[talentName] == nil then
+ br._G.print("|cffff0000Talent: |r" .. talentName .. "|cffff0000 with ID: |r" .. talentID .. " is not listed in the Talents Spell List.")
+ -- Check if spell ID matches
+ elseif (specTalents[talentName] ~= nil and specTalents[talentName] ~= talentID) or
+ (sharedTalents[talentName] ~= nil and sharedTalents[talentName] ~= talentID) or
+ (heroicTalents[talentName] ~= nil and heroicTalents[talentName] ~= talentID) then
+ local currentID = specTalents[talentName] or sharedTalents[talentName] or heroicTalents[talentName]
+ br._G.print("|cffff0000Talent found in Talent Spell List for |r" .. talentName .. "|cffff0000 with ID: |r" .. currentID .. "|cffff0000 it should be changed to ID: |r" .. talentID .. "|cffff0000.")
+ end
end
end
end
end
end
- return talents
- end
- -- Update Talent Info
- local function getTalentInfo()
- for specName, specID in pairs(br.lists.spec[class]) do
- if specID == spec and specName ~= "Initial" then
- return getAllTalents()
+ -- Map defined spell talents to talent state
+ for name, spellID in pairs(self.spells.talents) do
+ local entry = allTalents[spellID]
+ if entry then
+ self.talent[name] = entry.active
+ if entry.active and not br.api.wow.IsPassiveSpell(spellID) then
+ self.spells.abilities[name] = spellID
+ self.spells[name] = spellID
+ end
+ else
+ -- Only warn for actual specs, not Initial profiles (spec > 1400)
+ if spec < 1400 then
+ br._G.print("|cffff0000No talent found for: |r" .. name .. " (" .. tostring(spellID) .. ") |cffff0000in the talent spell list; please verify ID or remove.")
+ end
end
end
+
+ return allTalents
end
- -- Check if Hero Spec if Active
+ -- Check if Hero Spec if Active (Retail only)
local function getHeroTreeInfo()
+ if not br.isRetail then return end
+ if not br._G.C_ClassTalents or not br._G.C_ClassTalents.GetActiveHeroTalentSpec then return end
+ if not br.lists or not br.lists.heroSpec then return end
+
local playerClass = select(2, br._G.UnitClass('player'))
-- Retrieve the active hero talent spec ID
- local activeSpecID = C_ClassTalents.GetActiveHeroTalentSpec()
+ local activeSpecID = br._G.C_ClassTalents.GetActiveHeroTalentSpec()
+
-- Check if the specName exists in the br.lists.heroSpec table
for class, specs in pairs(br.lists.heroSpec) do
- -- print("Class: "..tostring(class).." Specs: "..tostring(specs))
for spec, specID in pairs(specs) do
- -- print("Spec: "..tostring(spec).." SpecID: "..tostring(specID))
if class == playerClass then
if self.heroTree == nil then self.heroTree = {} end
if self.heroTree[spec] == nil then self.heroTree[spec] = false end
@@ -341,8 +434,8 @@ function br.loader:new(spec, specName)
-- if not self.essence[k] then self.essence[k] = {} end
-- local essence = self.essence[k]
-- if not br._G.C_Spell.IsSpellPassive(v) then
- -- self.spells['abilities'][k] = select(7,br._G.GetSpellInfo(br._G.GetSpellInfo(v))) or v--heartEssence
- -- self.spells[k] = select(7,br._G.GetSpellInfo(br._G.GetSpellInfo(v))) or v--heartEssence
+ -- self.spells['abilities'][k] = select(7,br.api.wow.GetSpellInfo(br.api.wow.GetSpellInfo(v))) or v--heartEssence
+ -- self.spells[k] = select(7,br.api.wow.GetSpellInfo(br.api.wow.GetSpellInfo(v))) or v--heartEssence
-- end
-- br.api.essences(essence,k,v)
-- end
@@ -414,15 +507,43 @@ function br.loader:new(spec, specName)
if k ~= "rollTheBones" then
if self.buff == nil then self.buff = {} end
if self.buff[k] == nil then self.buff[k] = {} end
- -- if k == "bloodLust" then v = br.getLustID() end
- br.api.buffs(self.buff, k, v)
+ -- if k == "bloodLust" then v = br.functions.aura:getLustID() end
+ local resolvedID = v
+ if (br.isClassic or br.isBC) and type(v) == "table" then
+ if br.functions.spell and br.functions.spell.getHighestKnownRank then
+ resolvedID = br.functions.spell:getHighestKnownRank(v)
+ if not resolvedID then
+ resolvedID = v[1] -- Safety fallback
+ end
+ else
+ resolvedID = v[1]
+ end
+ -- Update the spell list to use resolved ID for icon references
+ self.spells.buffs[k] = resolvedID
+ self.spells[k] = resolvedID -- Also update base spells table
+ end
+ br.api.buffs(self.buff, k, resolvedID)
end
end
-- Make Debuff Functions from br.api.debuffs
for k, v in pairs(self.spells.debuffs) do
if self.debuff == nil then self.debuff = {} end
if self.debuff[k] == nil then self.debuff[k] = {} end
- br.api.debuffs(self.debuff[k], k, v)
+ local resolvedID = v
+ if (br.isClassic or br.isBC) and type(v) == "table" then
+ if br.functions.spell and br.functions.spell.getHighestKnownRank then
+ resolvedID = br.functions.spell:getHighestKnownRank(v)
+ if not resolvedID then
+ resolvedID = v[1] -- Safety fallback
+ end
+ else
+ resolvedID = v[1]
+ end
+ -- Update the spell list to use resolved ID for icon references
+ self.spells.debuffs[k] = resolvedID
+ self.spells[k] = resolvedID -- Also update base spells table
+ end
+ br.api.debuffs(self.debuff[k], k, resolvedID)
end
-- Make Units Functions from br.api.units
@@ -443,6 +564,14 @@ function br.loader:new(spec, specName)
br.api.unit(self)
end
+ -- Make Totem Functions from br.api.totem
+ if self.totem == nil then
+ self.totem = {}
+ if br.api.totem then
+ br.api.totem(self)
+ end
+ end
+
-- Make Pet Functions from br.api.pets
if self.pets ~= nil then
for k, v in pairs(self.pets) do
@@ -452,6 +581,30 @@ function br.loader:new(spec, specName)
end
end
+ -- Make Glyph Functions from br.api.glyph / br.api.glyphs
+ if self.glyph == nil then self.glyph = {} end
+ if br.api.glyph then
+ br.api.glyph(self)
+ end
+ if self.spells and self.spells.glyphs then
+ for k, v in pairs(self.spells.glyphs) do
+ self.glyph[k] = self.glyph[k] or {}
+ if br.api.glyphs then
+ br.api.glyphs(self.glyph, k, v)
+ end
+ -- Convenience alias: glyphOfFrostShock -> frostShock
+ if type(k) == "string" and k:sub(1, 7) == "glyphOf" then
+ local suffix = k:sub(8)
+ if suffix and suffix ~= "" then
+ local alias = suffix:sub(1, 1):lower() .. suffix:sub(2)
+ if self.glyph[alias] == nil then
+ self.glyph[alias] = self.glyph[k]
+ end
+ end
+ end
+ end
+ end
+
-- Cycle through Items List
for item, id in pairs(self.items) do --self.spells.items) do
if self.charges == nil then self.charges = {} end -- Item Charge Functions
@@ -473,7 +626,7 @@ function br.loader:new(spec, specName)
br.api.use(self.use, item, id)
- br.getHeirloomNeck()
+ br.functions.item:getHeirloomNeck()
end
-- Cycle through Abilities List
@@ -482,14 +635,46 @@ function br.loader:new(spec, specName)
if self.cd == nil then self.cd = {} end -- Spell Cooldown Functions
if self.spell == nil then self.spell = {} end -- Spell Functions
+ -- Classic WoW: If spell ID is a table (ranks), resolve to highest known rank
+ local resolvedID = id
+ if (br.isClassic or br.isBC) and type(id) == "table" then
+ -- Get highest known rank from the table
+ if br.functions.spell and br.functions.spell.getHighestKnownRank then
+ resolvedID = br.functions.spell:getHighestKnownRank(id)
+ if not resolvedID then
+ resolvedID = id[1] -- Safety fallback
+ end
+ else
+ -- Fallback to first rank if function not available
+ resolvedID = id[1]
+ end
+ -- Update the spell list to use resolved ID for icon references
+ self.spells.abilities[spell] = resolvedID
+ self.spells[spell] = resolvedID -- Also update base spells table
+ end
+
-- Build Cast Funcitons
- br.api.cast(self, spell, id)
+ br.api.cast(self, spell, resolvedID)
-- Build Spell Charges
- br.api.charges(self.charges, spell, id)
+ br.api.charges(self.charges, spell, resolvedID)
-- Build Spell Cooldown
- br.api.cd(self, spell, id)
+ br.api.cd(self, spell, resolvedID)
-- Build Spell
- br.api.spell(self, spell, id)
+ br.api.spell(self, spell, resolvedID)
+ end
+
+ -- Ensure racial cast helpers always exist, even when no racial spell is available.
+ if self.cast == nil then self.cast = {} end
+ if self.cast.able == nil then self.cast.able = {} end
+ if self.cast.racial == nil then
+ self.cast.racial = function()
+ return false
+ end
+ end
+ if self.cast.able.racial == nil then
+ self.cast.able.racial = function()
+ return false
+ end
end
-- Make UI Functions from br.api.ui
@@ -505,8 +690,8 @@ function br.loader:new(spec, specName)
end
end
- if spec == br._G.GetSpecializationInfo(br._G.GetSpecialization()) and (self.talent == nil or self.cast == nil) then
- getSpellsForSpec(spec); --[[getTalentInfo(); getAzeriteTraitInfo();]] getFunctions(); br.updatePlayerInfo = false
+ if spec == br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization()) and (self.talent == nil or self.cast == nil) then
+ getSpellsForSpec(spec); --[[getTalentInfo(); getAzeriteTraitInfo();]] getFunctions(); self.updatePlayer = false
end
------------------
--- OOC UPDATE ---
@@ -521,19 +706,19 @@ function br.loader:new(spec, specName)
--- UPDATE ---
--------------
- function self.update()
- if spec == br._G.GetSpecializationInfo(br._G.GetSpecialization()) then
+ function self:update()
+ if spec == br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization()) then
-- Call baseUpdate()
if not br._G.UnitAffectingCombat("player") then self.updateOOC() end
self.baseUpdate()
self.getBleeds()
-- Update Player Info on Init, Talent, and Level Change
- if br.updatePlayerInfo then
+ if self.updatePlayer then
getSpellsForSpec(spec)
-- getTalentInfo()
-- getAzeriteTraitInfo()
getFunctions()
- br.updatePlayerInfo = false
+ self.updatePlayer = false
end
self.getToggleModes()
self.startRotation()
@@ -549,7 +734,7 @@ function br.loader:new(spec, specName)
if k == "rake" or k == "rip" or k == "rupture" or k == "garrote" or k == "crimsonTempest" then
if self.debuff[k].bleed == nil then self.debuff[k].bleed = {} end
for l, _ in pairs(self.debuff[k].bleed) do
- if --[[not UnitAffectingCombat("player") or]] br.GetUnitIsDeadOrGhost(l) then
+ if --[[not UnitAffectingCombat("player") or]] br.functions.unit:GetUnitIsDeadOrGhost(l) then
self.debuff[k].bleed[l] = nil
elseif not self.debuff[k].exists(l, "EXACT") then
self.debuff[k].bleed[l] = 0
@@ -564,7 +749,7 @@ function br.loader:new(spec, specName)
if k == "crimsonTempest" or k == "garrote" or k == "rupture" then
if self.debuff[k].exsa == nil then self.debuff[k].exsa = {} end
for l, _ in pairs(self.debuff[k].exsa) do
- if --[[not UnitAffectingCombat("player") or]] br.GetUnitIsDeadOrGhost(l) then
+ if --[[not UnitAffectingCombat("player") or]] br.functions.unit:GetUnitIsDeadOrGhost(l) then
self.debuff[k].exsa[l] = nil
elseif not self.debuff[k].exists(l) then
self.debuff[k].exsa[l] = false
@@ -580,16 +765,16 @@ function br.loader:new(spec, specName)
---------------
function self.getToggleModes()
- for k, _ in pairs(br.data.settings[br.selectedSpec].toggles) do
+ for k, _ in pairs(br.data.settings[br.loader.selectedSpec].toggles) do
local toggle = k:sub(1, 1):lower() .. k:sub(2)
- self.ui.mode[toggle] = br.data.settings[br.selectedSpec].toggles[k]
- br.UpdateToggle(k, 0.25)
+ self.ui.mode[toggle] = br.data.settings[br.loader.selectedSpec].toggles[k]
+ br.functions.misc:UpdateToggle(k, 0.25)
end
end
-- Create the toggle defined within rotation files
function self.createToggles()
- br.GarbageButtons()
+ br.ui:GarbageButtons()
if self.rotation ~= nil and self.rotation.toggles ~= nil then
self.rotation.toggles()
else
@@ -621,21 +806,21 @@ function br.loader:new(spec, specName)
local names
function self.createOptions()
-- if br.ui:closeWindow("profile")
- for i = 1, #br.data.settings[br.selectedSpec] do
- local thisProfile = br.data.settings[br.selectedSpec][i]
- if thisProfile ~= br.data.settings[br.selectedSpec][br.selectedProfile] then
+ for i = 1, #br.data.settings[br.loader.selectedSpec] do
+ local thisProfile = br.data.settings[br.loader.selectedSpec][i]
+ if thisProfile ~= br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile] then
br.ui:closeWindow("profile")
end
end
- if br.data.settings[br.selectedSpec][br.selectedProfile] == nil then br.data.settings[br.selectedSpec][br.selectedProfile] = {} end
+ if br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile] == nil then br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile] = {} end
br.ui:createProfileWindow(self.profile)
-- Get the names of all profiles and create rotation dropdown
if names == nil then names = {} end
table.wipe(names)
- if #br.rotations[spec] > 0 then
- for i = 1, #br.rotations[spec] do
- local thisName = br.rotations[spec][i].name
+ if #br.loader.rotations[spec] > 0 then
+ for i = 1, #br.loader.rotations[spec] do
+ local thisName = br.loader.rotations[spec][i].name
br._G.tinsert(names, thisName)
end
end
@@ -664,7 +849,7 @@ function br.loader:new(spec, specName)
--
-- -- Only add profile pages if they are found
if self.rotation.options ~= nil then
- br.insertTableIntoTable(optionTable, self.rotation.options())
+ br.functions.custom:insertTableIntoTable(optionTable, self.rotation.options())
end
-- Create pages dropdown
@@ -678,96 +863,6 @@ function br.loader:new(spec, specName)
--- CUSTOM FUNCTIONS ---
------------------------
- function br.useAoE()
- local rotation = self.ui.mode.rotation
- if (rotation == 1 and #self.enemies.get(8) >= 3) or rotation == 2 then
- return true
- else
- return false
- end
- end
-
- function br.useCDs()
- local cooldown = self.ui.mode.cooldown
- if (cooldown == 1 and br.isBoss()) or cooldown == 2 or (cooldown == 4 and br.hasBloodLust()) then
- return true
- else
- return false
- end
- end
-
- function br.useDefensive()
- if self.ui.mode.defensive == 1 then
- return true
- else
- return false
- end
- end
-
- function br.useInterrupts()
- if self.ui.mode.interrupt == 1 then
- return true
- else
- return false
- end
- end
-
- function br.useMfD()
- if self.ui.mode.mfd == 1 then
- return true
- else
- return false
- end
- end
-
- function br.useRollForTB()
- if self.ui.mode.RerollTB == 1 then
- return true
- else
- return false
- end
- end
-
- function br.useRollForOne()
- if self.ui.mode.RollForOne == 1 then
- return true
- else
- return false
- end
- end
-
- function br.ComboMaxSpend()
- return br.player.talent.deeperStratagem and 6 or 5
- end
-
- function br.ComboSpend()
- return math.min(br.player.power.comboPoints.amount(), br.ComboMaxSpend())
- end
-
- function br.mantleDuration()
- if br.hasEquiped(144236) then
- --if br.player.buff.masterAssassinsInitiative.remain("player") > 100 or br.player.buff.masterAssassinsInitiative.remain("player") < 0 then
- if br.player.buff.masterAssassinsInitiative.exists("player") and (br.getBuffRemain("player", 235027) > 100 or br.getBuffRemain("player", 235027) < 100) then
- return br.player.cd.global.remain() + 5
- else
- --return br.player.buff.masterAssassinsInitiative.remain("player")
- if br.getBuffRemain("player", 235027) >= 0 and br.getBuffRemain("player", 235027) < 0.1 then
- return 0
- else
- return br.getBuffRemain("player", 235027)
- end
- end
- else
- return 0
- end
- end
-
- function br.BleedTarget()
- return (br.player.debuff.garrote.exists("target") and 1 or 0) +
- (br.player.debuff.rupture.exists("target") and 1 or 0) +
- (br.player.debuff.internalBleeding.exists("target") and 1 or 0)
- end
-
-- Debugging
br.debug.cpu:updateDebug(loadStart, "rotation.loadTime")
-----------------------------
diff --git a/System/Loader/cCharacter.lua b/System/Loader/cCharacter.lua
index f264dc311e..49138a3478 100644
--- a/System/Loader/cCharacter.lua
+++ b/System/Loader/cCharacter.lua
@@ -1,9 +1,11 @@
local _, br = ...
--- Character Class
-- All classes inherit from the base class /cCharacter.lua
-br.cCharacter = {}
+br.loader.cCharacter = br.loader.cCharacter or {}
+local cCharacter = br.loader.cCharacter
+
-- Creates new character with given class
-function br.cCharacter:new(class)
+function cCharacter:new()
local self = {}
self.actionList = {} -- Action Lists
self.augmentRune = {
@@ -14,6 +16,7 @@ function br.cCharacter:new(class)
Legion = 224001
}
self.artifact = {} -- Artifact Perk IDs
+ self.bagsUpdated = true -- Bags updated flag
-- self.buff = {} -- Buffs API
-- self.debuff = {} -- Debuffs API
self.class = select(2, br._G.UnitClass("player")) -- Class
@@ -25,6 +28,7 @@ function br.cCharacter:new(class)
--self.enemies = {} -- Number of Enemies around player (must be overwritten by cCLASS or cSPEC)
self.essence = {} -- Azerite Essence
-- self.equiped = {} -- Item Equips
+ self.equipHasChanged = true -- Flag for equipment changes
self.gcd = 1.5 -- Global Cooldown
self.gcdMax = 1.5 -- GLobal Max Cooldown
self.glyph = {} -- Glyphs
@@ -62,6 +66,7 @@ function br.cCharacter:new(class)
self.health = 100 -- Health Points in %
self.ignoreCombat = false -- Ignores combat status if set to true
self.inCombat = false -- if is in combat
+ self.inCombatPrevious = false -- Previous combat state for tracking combat start
self.instance = select(2, br._G.IsInInstance()) -- Get type of group we are in (none, party, instance, raid, etc)
self.level = 0 -- Player Level
self.moving = false -- Moving event
@@ -94,7 +99,7 @@ function br.cCharacter:new(class)
-- Get Character Info
self.getCharacterInfo()
-- Get Consumables
- if br.bagsUpdated then
+ if self.bagsUpdated then
self.potion.action = {}
self.potion.agility = {} -- Agility Potions
self.potion.armor = {} -- Armor Potions
@@ -114,7 +119,7 @@ function br.cCharacter:new(class)
self.flask.stamina = {}
self.flask.strength = {}
self.getConsumables() -- Find All The Tasty Things!
- br.bagsUpdated = false
+ self.bagsUpdated = false
end
-- Get selected rotation
self.getRotation()
@@ -130,10 +135,10 @@ function br.cCharacter:new(class)
function self.getCharacterInfo()
self.gcd = self.getGlobalCooldown()
self.gcdMax = self.getGlobalCooldown(true)
- self.health = br.getHP("player")
+ self.health = br.functions.unit:getHP("player")
self.instance = select(2, br._G.IsInInstance())
self.level = br._G.UnitLevel("player") -- TODO: EVENT - UNIT_LEVEL
- self.spec = select(2, br._G.GetSpecializationInfo(br._G.GetSpecialization())) or "None"
+ self.spec = select(2, br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization())) or "None"
self.currentPet = br._G.UnitCreatureFamily("pet") or "None"
if self.currentPet ~= "None" then
self.petId = tonumber(br._G.UnitGUID("pet"):match("-(%d+)-%x+$"), 10)
@@ -147,37 +152,37 @@ function br.cCharacter:new(class)
function self.baseUpdateOOC()
-- Updates special Equip like set bonuses
self.baseGetEquip()
- if br.getOptionCheck("Queue Casting") and #self.queue ~= 0 then
+ if br.functions.misc:getOptionCheck("Queue Casting") and #self.queue ~= 0 then
self.queue = {} -- Reset Queue Casting Table out of combat
br._G.print("Out of Combat - Queue List Cleared")
end
- self.ignoreCombat = br.getOptionCheck("Ignore Combat")
+ self.ignoreCombat = br.functions.misc:getOptionCheck("Ignore Combat")
end
-- Updates toggle data
-- TODO: here should only happen generic ones like Defensive etc.
function self.getBaseToggleModes()
- self.ui.mode.rotation = br.data.settings[br.selectedSpec].toggles["Rotation"]
- self.ui.mode.cooldown = br.data.settings[br.selectedSpec].toggles["Cooldown"]
- self.ui.mode.defensive = br.data.settings[br.selectedSpec].toggles["Defensive"]
- self.ui.mode.interrupt = br.data.settings[br.selectedSpec].toggles["Interrupt"]
+ self.ui.mode.rotation = br.data.settings[br.loader.selectedSpec].toggles["Rotation"]
+ self.ui.mode.cooldown = br.data.settings[br.loader.selectedSpec].toggles["Cooldown"]
+ self.ui.mode.defensive = br.data.settings[br.loader.selectedSpec].toggles["Defensive"]
+ self.ui.mode.interrupt = br.data.settings[br.loader.selectedSpec].toggles["Interrupt"]
end
-- Returns the Global Cooldown time
function self.getGlobalCooldown(max)
- return br.getGlobalCD(max)
+ return br.functions.spell:getGlobalCD(max)
end
-- Starts auto attack when in melee range and facing enemy
function self.startMeleeAttack()
- if self.inCombat and (br.isInMelee() and br.getFacing("player", "target") == true) then
+ if self.inCombat and (br.functions.range:isInMelee() and br.functions.unit:getFacing("player", "target") == true) then
br._G.StartAttack('target')
end
end
function self.tankAggro()
if self.instance == "raid" or self.instance == "party" then
- local tanksTable = br.getTanksTable()
+ local tanksTable = br.engines.healingEngineFunctions:getTanksTable()
if tanksTable ~= nil then
for i = 1, #tanksTable do
if br._G.UnitAffectingCombat(tanksTable[i].unit) and tanksTable[i].distance < 40 then
@@ -191,19 +196,38 @@ function br.cCharacter:new(class)
-- Returns if in combat
function self.getInCombat()
+ local wasInCombat = self.inCombat
+
if br._G.UnitAffectingCombat("player") or self.ignoreCombat or
- (br.isChecked("Tank Aggro = Player Aggro") and self.tankAggro()) or
+ (br.functions.misc:isChecked("Tank Aggro = Player Aggro") and self.tankAggro()) or
(br._G.GetNumGroupMembers() > 1 and (br._G.UnitAffectingCombat("player") or br._G.UnitAffectingCombat("target")))
then
self.inCombat = true
+
+ -- Detect combat start transition (was not in combat, now in combat)
+ if not self.inCombatPrevious and self.inCombat then
+ if self.ui and self.ui.debug then
+ self.ui.debug("Combat Started")
+ end
+ end
else
self.inCombat = false
+
+ -- Detect combat end transition (was in combat, now not in combat)
+ if self.inCombatPrevious and not self.inCombat then
+ if self.ui and self.ui.debug then
+ self.ui.debug("Combat Ended")
+ end
+ end
end
+
+ -- Update previous combat state for next check
+ self.inCombatPrevious = self.inCombat
end
-- Rotation selection update
function self.getRotation()
- self.selectedRotation = br.selectedProfile
+ self.selectedRotation = br.loader.selectedProfile
if br.rotationChanged then
self.createOptions()
@@ -218,6 +242,11 @@ function br.cCharacter:new(class)
-- dont check if player is casting to allow off-cd usage and cast while other spell is casting
-- if pause(true) then return end
+ -- Halt all rotations during loading screens and transitions
+ if br.disableControl or br.disablePulse then
+ return
+ end
+
if self.rotation ~= nil and self.rotation.run ~= nil then
self.runModules()
self.rotation.run()
@@ -226,7 +255,7 @@ function br.cCharacter:new(class)
end
-- Debugging
br.debug.cpu:updateDebug(startTime, "rotation")
- -- if br.isChecked("Debug Timers") then
+ -- if br.functions.misc:isChecked("Debug Timers") then
-- -- br.debug.cpu.rotation = {
-- -- maxTimeOoC = 0,
-- -- minTimeOoC = 999,
@@ -248,12 +277,12 @@ function br.cCharacter:new(class)
if self.equiped == nil then
self.equiped = {}
end
- if self.equiped.t17 == nil or br.equipHasChanged == nil or br.equipHasChanged then
+ if self.equiped.t17 == nil or self.equipHasChanged == nil or self.equipHasChanged then
for i = 17, 21 do
if self.equiped["t" .. i] == nil then
self.equiped["t" .. i] = 0
end
- self.equiped["t" .. i] = br.TierScan("T" .. i) or 0
+ self.equiped["t" .. i] = br.functions.item:TierScan("T" .. i) or 0
end
-- Checks class trinket
local classTrinket = {
@@ -269,24 +298,24 @@ function br.cCharacter:new(class)
warlock = 124522, -- Fragment of the Dark Star
warrior = 124523 -- Worldbreaker's Resolve
}
- self.equiped.t18_classTrinket = br.isTrinketEquipped(classTrinket[string.lower(self.class)])
+ self.equiped.t18_classTrinket = br.functions.custom:isTrinketEquipped(classTrinket[string.lower(self.class)])
- br.equipHasChanged = false
+ self.equipHasChanged = false
end
end
-- Sets the racial
function self.getRacial()
- return br.getRacial()
+ return br.functions.spell:getRacial()
end
-- Casts the racial
function self.castRacial()
- if br.getSpellCD(self.racial) == 0 and br.getOptionValue("Racial") then
+ if br.functions.spell:getSpellCD(self.racial) == 0 and br.functions.misc:getOptionValue("Racial") then
if self.race == "Pandaren" or self.race == "Goblin" then
- return br.castSpell("target", self.racial, true, false) == true
+ return br.functions.cast:castSpell("target", self.racial, true, false) == true
else
- return br.castSpell("player", self.racial, true, false) == true
+ return br.functions.cast:castSpell("player", self.racial, true, false) == true
end
end
end
@@ -303,11 +332,9 @@ function br.cCharacter:new(class)
--- Base Wrap ---
-----------------
local section_base = br.ui:createSection(br.ui.window.profile, "Base Options")
- br.ui:createCheckbox(section_base, "Cast Debug", "Shows information about how the bot is casting.")
- br.ui:createCheckbox(section_base, "Threat Debug", "Shows information about threat and threat changes.")
br.ui:createCheckbox(section_base, "Ignore Combat", "Checking this will make BR think it is always in combat")
br.ui:createCheckbox(section_base, "Mute Queue", "Mute messages from Smart Queue and Queue Casting")
- br.ui:createDropdown(section_base, "Pause Mode", br.dropOptions.Toggle, 2,
+ br.ui:createDropdown(section_base, "Pause Mode", br.ui.dropOptions.Toggle, 2,
"Define a key which pauses the rotation.")
br.ui:checkSectionState(section_base)
-------------------
@@ -318,12 +345,17 @@ function br.cCharacter:new(class)
br.ui:createText(section_module, "|cffff0000in Rotation Options")
br.ui:createText(section_module, "===General Options===")
self.module.autoKeystone(section_module)
+ self.module.Racial(section_module)
+ -- Two Forms (Worgen) - shared module option
+ self.module.TwoForms(section_module)
br.ui:createText(section_module, "===Healing Options===")
-- Basic Healing Module
self.module.BasicHealing(section_module)
br.ui:createText(section_module, "===Combat Options===")
-- Combat Potion Module
self.module.CombatPotionUp(section_module)
+ -- Flask Module
+ self.module.FlaskUp(self.primaryStat, section_module)
-- Imbue Module
self.module.ImbueUp(section_module)
-- Phial Module
@@ -346,12 +378,16 @@ function br.cCharacter:new(class)
-- Module - Imbue Up
self.module.ImbueUp()
end
- if self.unit.inCombat() and self.ui.useCDs() then
+ if self.unit.inCombat() then --and self.ui.useCDs() then
-- Module - Combatpotion Up
self.module.CombatPotionUp()
-- Module - Basic Trinkets
- self.module.BasicTrinkets()
+ if not self.localTrinkets then
+ self.module.BasicTrinkets()
+ end
end
+ -- Module - Two Forms (Worgen)
+ self.module.TwoForms()
end
-- Returns and sets highest stat, which will be the primary stat
@@ -391,14 +427,15 @@ function br.cCharacter:new(class)
local itemInfo = {
--Collect Item Data
itemID = itemID,
- itemCD = br._G.GetItemCooldown(itemID),
+ itemCD = br._G.C_Container.GetItemCooldown(itemID),
itemName = br._G.GetItemInfo(itemID),
minLevel = select(5, br._G.GetItemInfo(itemID)),
itemType = select(7, br._G.GetItemInfo(itemID)),
itemEffect = itemEffect,
itemCount = br._G.GetItemCount(itemID)
}
- if itemInfo.itemType == "Potions" and self.level >= itemInfo.minLevel then -- Is the item a Potion and am I level to use it?
+ -- print("Found Item: " .. itemInfo.itemName .. " (" .. itemInfo.itemID .. ") x" .. itemInfo.itemCount)
+ if itemInfo.itemType == "Potion" and self.level >= itemInfo.minLevel then -- Is the item a Potion and am I level to use it?
local potionList = {
{ ptype = "action", effect = "Action" },
{ ptype = "agility", effect = "Agility" },
diff --git a/System/LootManager.lua b/System/LootManager.lua
deleted file mode 100644
index be57d23d93..0000000000
--- a/System/LootManager.lua
+++ /dev/null
@@ -1,216 +0,0 @@
-local _, br = ...
--- Sell Greys Macros
-SLASH_Greys1 = "/grey"
-SLASH_Greys2 = "/greys"
-function br._G.SlashCmdList.Greys(msg, editbox)
- br.SellGreys()
-end
-
-function br.SellGreys()
- for bag = 0, 4 do
- for slot = 1, br._G.C_Container.GetContainerNumSlots(bag) do
- local item = br._G.C_Container.GetContainerItemLink(bag, slot)
- if item then
- -- Is it grey quality item?
- if string.find(item, br.qualityColors.grey) ~= nil then
- local containerItemInfo = br._G.C_Container.GetContainerItemInfo(bag, slot)
- local greyPrice = select(11, br._G.GetItemInfo(item)) * containerItemInfo.stackCount
- if greyPrice > 0 then
- br._G.C_Container.PickupContainerItem(bag, slot)
- br._G.PickupMerchantItem()
- end
- end
- end
- end
- end
- br._G.RepairAllItems(1)
- br._G.RepairAllItems(0)
- br.ChatOverlay("Sold Greys.")
-end
-
--- Dump Greys Macros
-SLASH_DumpGrey1 = "/dumpgreys"
-SLASH_DumpGrey2 = "/dg"
-function br._G.SlashCmdList.DumpGrey(msg, editbox)
- br.DumpGreys(1)
-end
-
-function br.DumpGreys(Num)
- local greyTable = {}
- for bag = 0, 4 do
- for slot = 1, br._G.C_Container.GetContainerNumSlots(bag) do
- local item = br._G.C_Container.GetContainerItemLink(bag, slot)
- if item then
- -- Is it grey quality item?
- if string.find(item, br.qualityColors.grey) ~= nil then
- local containerItemInfo = br._G.C_Container.GetContainerItemInfo(bag, slot)
- local greyPrice = select(11, br._G.GetItemInfo(item)) * containerItemInfo.stackCount
- if greyPrice > 0 then
- br._G.tinsert(greyTable, { Bag = bag, Slot = slot, Price = greyPrice, Item = item })
- end
- end
- end
- end
- end
- table.sort(
- greyTable,
- function(x, y)
- if x.Price and y.Price then -- If both 'x' and 'y' have Prices, it compares them as before
- return x.Price < y.Price
- elseif x.Price then -- If only 'x' has a Price, it assumes 'x' is more expensive and goes after 'y'
- return false
- else -- if neither 'x' nor 'y' has a Price, or only 'y' has a Price, it assumes 'x' is cheaper and goes before 'y'
- return true
- end
- end
- )
- for i = 1, Num do
- if greyTable[i] ~= nil then
- br._G.C_Container.PickupContainerItem(greyTable[i].Bag, greyTable[i].Slot)
- br._G.DeleteCursorItem()
- br._G.print("|cffFF0000Removed Grey Item:" .. greyTable[i].Item)
- end
- end
-end
-
-------------------
--- Loot Manager --
-------------------
-br.lootManager = {}
-br.lM = br.lootManager
--- Debug
-function br.lootManager:debug(message)
- if message and br.lM.oldMessage ~= message then
- br.addonDebug(" " .. (math.floor(br._G.GetTime() * 1000) / 1000) .. " " .. message, true)
- br.lM.oldMessage = message
- end
-end
-
--- Check if availables bag slots, return true if at least 1 free bag space
-function br.lootManager:emptySlots()
- local openSlots = 0
- for i = 0, 4 do --Let's look at each bag
- local numBagSlots = br._G.C_Container.GetContainerNumSlots(i)
- if numBagSlots > 0 then -- Only look for slots if bag present
- openSlots = openSlots + select(1, br._G.C_Container.GetContainerNumFreeSlots(i))
- end
- end
- return openSlots
-end
-
-local looting = false
-function br.lootManager:getLoot(lootUnit)
- -- if we have a unit to loot, check if its time to
- if br.timer:useTimer("getLoot", br.getOptionValue("Auto Loot")) then
- if br.getDistance(lootUnit) < 7 then
- if not looting then
- looting = true
- br.lM:debug("Looting " .. br._G.UnitName(lootUnit))
- -- print("Looting " .. br._G.UnitName(lootUnit))
- br._G.InteractUnit(lootUnit)
- -- Manually loot if Auto Loot Interface Option not set
- if br._G.GetCVar("AutoLootDefault") == "0" then
- if br._G.LootFrame:IsShown() then
- for l = 1, br._G.GetNumLootItems() do
- if br._G.LootSlotHasItem(l) then
- br._G.LootSlot(l)
- end
- end
- br._G.CloseLoot()
- end
- end
- end
- end
- if not br.isInCombat("player") and looting then
- br._G.ClearTarget()
- end
- looting = false
- br.lM.lootUnit = nil
- br.lootable = {}
- return
- end
-end
-
--- function br.lootManager:getLoot(lootUnit)
--- local looting = false
--- -- if we have a unit to loot, check if its time to
--- if br.timer:useTimer("getLoot", br.getOptionValue("Auto Loot")) then
--- if not looting then
--- looting = true
--- --Print("Looting "..UnitName(lootUnit))
--- lM:debug("Looting " .. UnitName(lootUnit))
--- InteractUnit(lootUnit)
--- -- Manually loot if Auto Loot Interface Option not set
--- if GetCVar("AutoLootDefault") == "0" then
--- if LootFrame:IsShown() then
--- for l = 1, GetNumLootItems() do
--- if LootSlotHasItem(l) then
--- LootSlot(l)
--- end
--- end
--- CloseLoot()
--- end
--- end
--- -- Clean Up
--- ClearTarget()
--- looting = false
--- lM.lootUnit = nil
--- br.lootable = {}
--- return
--- end
--- end
--- end
--- function br.lootManager:findLoot()
--- if br.timer:useTimer("findLoot", br.getOptionValue("Auto Loot")) then
--- lM:debug("Find Unit")
--- for k, v in pairs(br.lootable) do
--- local thisUnit = br.lootable[k].unit
--- local hasLoot, canLoot = CanLootUnit(thisUnit)
--- if br.GetObjectExists(thisUnit) and canLoot then
--- --Print("Should loot "..UnitName(thisUnit))
--- lM:debug("Should loot " .. UnitName(thisUnit))
--- lM:getLoot(thisUnit)
--- break
--- end
--- end
--- end
--- end
-function br.lootManager:lootCount()
- local lootCount = 0
- br.lM.lootUnit = nil
- for k, _ in pairs(br.lootable) do
- if br.lootable[k] ~= nil then
- local thisUnit = br.lootable[k].unit
- local hasLoot = br._G.CanLootUnit(br.lootable[k].guid)
- if br.GetObjectExists(thisUnit) and hasLoot then
- lootCount = lootCount + 1
- br.lM.lootUnit = br.lootable[k].unit
- break
- end
- end
- end
- return lootCount
-end
-
-function br.autoLoot()
- if br.getOptionCheck("Auto Loot") then
- --br.player.enemies.get(40)
- if (not br.isInCombat("player") or br.player.enemies.get(10)[1] == nil) then
- -- start loot manager
- if br.lM and br.lM:lootCount() > 0 then
- -- print("Looking for Loot!")
- if br.lM:emptySlots() ~= 0 then
- if br._G.UnitCastingInfo("player") == nil and br._G.UnitChannelInfo("player") == nil
- and not br._G.IsMounted("player") and br._G.GetUnitSpeed("player") == 0
- and br.timer:useTimer("lootTimer", 1) and not br._G.UnitIsDeadOrGhost("player")
- then
- -- print("Getting Loot")
- br.lM:getLoot(br.lM.lootUnit)
- end
- else
- br.ChatOverlay("Bags are full, nothing will be looted!")
- end
- end
- end
- end
-end
diff --git a/System/Misc/ProfessionHelper/ProfessionHelper.lua b/System/Misc/ProfessionHelper/ProfessionHelper.lua
index 3d6729a78a..e2c06e9d4e 100644
--- a/System/Misc/ProfessionHelper/ProfessionHelper.lua
+++ b/System/Misc/ProfessionHelper/ProfessionHelper.lua
@@ -1,8 +1,11 @@
local _, br = ...
-function br.ProfessionHelper()
- if br.isChecked("Profession Helper") then
- if not br.isInCombat("player") and not (br._G.IsMounted() or br._G.IsFlying()) then
- local lootDelay = br.getValue("Profession Helper")
+br.misc.ProfessionHelper = br.misc.ProfessionHelper or {}
+local ProfessionHelper = br.misc.ProfessionHelper
+
+function ProfessionHelper:ProfessionHelper()
+ if br.functions.misc:isChecked("Profession Helper") then
+ if not br.functions.misc:isInCombat("player") and not (br._G.IsMounted() or br._G.IsFlying()) then
+ local lootDelay = br.functions.misc:getValue("Profession Helper")
local function processThatTable(thisTable, spell)
for i = 1, #thisTable do
local thisItem = thisTable[i]
@@ -15,7 +18,7 @@ function br.ProfessionHelper()
-- local itemName, _, _, _, _, itemClass, itemSubClass = br._G.GetItemInfo(itemLink);
local itemID = select(10, br._G.C_Container.GetContainerItemIndo(bagID, slotID)) --tonumber(string.match(containerItemInfo, "Hitem:(%d+)"))
if itemID == thisItem then
- br._G.RunMacroText("/cast " .. br._G.GetSpellInfo(spell))
+ br._G.RunMacroText("/cast " .. br.api.wow.GetSpellInfo(spell))
br._G.RunMacroText("/use item:" .. itemID)
br.lootTimer = br._G.GetTime()
end
@@ -36,8 +39,8 @@ function br.ProfessionHelper()
------------------------------------------------------------------------------------------------------
-- Milling -------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
- if br.isChecked("Mill Herbs") and br._G.IsSpellKnown(51005) then
- local millMode = br.getValue("Mill Herbs")
+ if br.functions.misc:isChecked("Mill Herbs") and br._G.IsSpellKnown(51005) then
+ local millMode = br.functions.misc:getValue("Mill Herbs")
if millMode == 7 or millMode == 1 then
local tableMillSL = {
168586, -- Rising Glory
@@ -107,8 +110,8 @@ function br.ProfessionHelper()
------------------------------------------------------------------------------------------------------
-- Prospecting ---------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
- if br.isChecked("Prospect Ores") and br._G.IsSpellKnown(31252) then
- local prospectMode = br.getValue("Prospect Ores")
+ if br.functions.misc:isChecked("Prospect Ores") and br._G.IsSpellKnown(31252) then
+ local prospectMode = br.functions.misc:getValue("Prospect Ores")
if prospectMode == 7 or prospectMode == 1 then
local tableProspectSL = {
171828, -- Laestrite Ore
@@ -163,7 +166,7 @@ function br.ProfessionHelper()
------------------------------------------------------------------------------------------------------
-- Disenchant ----------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
- if br.isChecked("Disenchant") and br._G.IsSpellKnown(13262) then
+ if br.functions.misc:isChecked("Disenchant") and br._G.IsSpellKnown(13262) then
-- list of items to me DE
local tableDisenchant = {
90905, -- JC Blue Neck ilvl 415
@@ -194,7 +197,7 @@ function br.ProfessionHelper()
------------------------------------------------------------------------------------------------------
-- Leather Scraps-------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
- if br.isChecked("Leather Scraps") then
+ if br.functions.misc:isChecked("Leather Scraps") then
-- Raw Beast Hide Scraps
if br._G.GetItemCount(110610, false, false) >= 10 then
if br.lootTimer == nil or br.lootTimer <= br._G.GetTime() - lootDelay then
@@ -208,7 +211,7 @@ function br.ProfessionHelper()
------------------------------------------------------------------------------------------------------
-- Lockboxes -----------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
- if br.isChecked("Lockboxes") then
+ if br.functions.misc:isChecked("Lockboxes") then
local tableLockBox = {
121331 -- Leystone Lockbox
}
@@ -216,7 +219,7 @@ function br.ProfessionHelper()
local thisItem = tableLockBox[i]
if br._G.GetItemCount(thisItem, false, false) >= 1 then
if br.lootTimer == nil or br.lootTimer <= br._G.GetTime() - lootDelay and not br._G.LootFrame:IsShown() then
- br._G.CastSpellByName(br._G.GetSpellInfo(1804), "player")
+ br._G.CastSpellByName(br.api.wow.GetSpellInfo(1804), "player")
br._G.UseItemByName(tostring(select(1, br._G.GetItemInfo(thisItem))))
br._G.C_Timer.After(1.5,
function() br._G.UseItemByName(tostring(select(1, br._G.GetItemInfo(thisItem)))) end)
@@ -236,7 +239,7 @@ function br.ProfessionHelper()
------------------------------------------------------------------------------------------------------
-- Fish Oil ------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
- if br.isChecked("Fish Oil") then
+ if br.functions.misc:isChecked("Fish Oil") then
local tableFish = {
152545, -- Frenzied Fangtooth
152547, -- Great Sea Catfish
@@ -263,12 +266,12 @@ function br.ProfessionHelper()
end
end
-function br.fishing()
- if br.isChecked("Fishing") and br.unlocked --[[EasyWoWToolbox ~= nil]] and br.getOptionValue("Fishing") == 1 then
+function ProfessionHelper:fishing()
+ if br.functions.misc:isChecked("Fishing") and br.unlocked --[[EasyWoWToolbox ~= nil]] and br.functions.misc:getOptionValue("Fishing") == 1 then
if not br._G.IsHackEnabled("fish") then
br._G.SetHackEnabled("fish", true)
end
- elseif br.isChecked("Fishing") and br.unlocked --[[EasyWoWToolbox ~= nil]] and br.getOptionValue("Fishing") == 2 then
+ elseif br.functions.misc:isChecked("Fishing") and br.unlocked --[[EasyWoWToolbox ~= nil]] and br.functions.misc:getOptionValue("Fishing") == 2 then
if br._G.IsHackEnabled("fish") then
br._G.SetHackEnabled("fish", false)
end
@@ -276,47 +279,47 @@ function br.fishing()
------------------------------------------------------------------------------------------------------
-- Bait ----------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
- if br.isChecked("Bait") then
- local bait = br.getValue("Bait")
+ if br.functions.misc:isChecked("Bait") then
+ local bait = br.functions.misc:getValue("Bait")
if bait == 1 then -- Lost Sole Bait
- if br.hasItem(173038) and br.getBuffRemain("player", 331688) == 0 then
- if br.canUseItem(173038) then
- br.useItem(173038)
+ if br.functions.item:hasItem(173038) and br.functions.aura:getBuffRemain("player", 331688) == 0 then
+ if br.functions.item:canUseItem(173038) then
+ br.functions.item:useItem(173038)
end
end
end
if bait == 2 then -- Silvergill Pike Bait
- if br.hasItem(173040) and br.getBuffRemain("player", 331690) == 0 then
- if br.canUseItem(173040) then
- br.useItem(173040)
+ if br.functions.item:hasItem(173040) and br.functions.aura:getBuffRemain("player", 331690) == 0 then
+ if br.functions.item:canUseItem(173040) then
+ br.functions.item:useItem(173040)
end
end
end
if bait == 3 then -- Pocked Bonefish Bait
- if br.hasItem(173041) and br.getBuffRemain("player", 331695) == 0 then
- if br.canUseItem(173041) then
- br.useItem(173041)
+ if br.functions.item:hasItem(173041) and br.functions.aura:getBuffRemain("player", 331695) == 0 then
+ if br.functions.item:canUseItem(173041) then
+ br.functions.item:useItem(173041)
end
end
end
if bait == 4 then -- Iridescent Amberjack Bait
- if br.hasItem(173039) and br.getBuffRemain("player", 331692) == 0 then
- if br.canUseItem(173039) then
- br.useItem(173039)
+ if br.functions.item:hasItem(173039) and br.functions.aura:getBuffRemain("player", 331692) == 0 then
+ if br.functions.item:canUseItem(173039) then
+ br.functions.item:useItem(173039)
end
end
end
if bait == 5 then -- Spinefin Piranha Bait
- if br.hasItem(173042) and br.getBuffRemain("player", 331699) == 0 then
- if br.canUseItem(173042) then
- br.useItem(173042)
+ if br.functions.item:hasItem(173042) and br.functions.aura:getBuffRemain("player", 331699) == 0 then
+ if br.functions.item:canUseItem(173042) then
+ br.functions.item:useItem(173042)
end
end
end
if bait == 6 then -- Elysian Thade Bait
- if br.hasItem(173043) and br.getBuffRemain("player", 331698) == 0 then
- if br.canUseItem(173043) then
- br.useItem(173043)
+ if br.functions.item:hasItem(173043) and br.functions.aura:getBuffRemain("player", 331698) == 0 then
+ if br.functions.item:canUseItem(173043) then
+ br.functions.item:useItem(173043)
end
end
end
diff --git a/System/Misc/Zu/BossManager.lua b/System/Misc/Zu/BossManager.lua
index 299628eba0..34acd03c93 100644
--- a/System/Misc/Zu/BossManager.lua
+++ b/System/Misc/Zu/BossManager.lua
@@ -1,10 +1,13 @@
local _, br = ...
-function br.bossHelper()
+br.misc.Zu = br.misc.Zu or {}
+local Zu = br.misc.Zu
+
+function Zu:bossHelper()
local function clickHelper(ID)
for _,v in pairs(br.player.enemies.get(40,nil,true)) do
- if ID == br.getUnitID(v) then
+ if ID == br.functions.unit:getUnitID(v) then
local distance = br._G.GetDistanceBetweenObjects("player", v)
- if distance < 10 and br.timer:useTimer("Action Delay", br.getOptionValue("Catcher/Snatcher Delay")) then
+ if distance < 10 and br.debug.timer:useTimer("Action Delay", br.functions.misc:getOptionValue("Catcher/Snatcher Delay")) then
br._G.InteractUnit(v)
end
end
@@ -16,7 +19,7 @@ function br.bossHelper()
clickHelper(130099)
end
-- Automatic bomb catcher
- if br.player.ui.checked("De Other Side - Bomb Snatcher") and br.getCurrentZoneId() == 2291 then
+ if br.player.ui.checked("De Other Side - Bomb Snatcher") and br.functions.custom:getCurrentZoneId() == 2291 then
clickHelper(164561)
end
end
diff --git a/System/Misc/foodflaskpot.lua b/System/Misc/foodflaskpot.lua
index 4a3da4f56d..00eaceee99 100644
--- a/System/Misc/foodflaskpot.lua
+++ b/System/Misc/foodflaskpot.lua
@@ -1,4 +1,7 @@
local _, br = ...
+br.misc.foodflaskpot = br.misc.foodflaskpot or {}
+local foodflaskpot = br.misc.foodflaskpot
+
local agilityPotion = br.player.potion.wod.agilityBasic
local intellectPotion = br.player.potion.wod.intellectBasic
local strengthPotion = br.player.potion.wod.strengthBasic
@@ -27,13 +30,13 @@ local strengthFoodBuff = 0
local staminaFoodBuff = 0
--playerHasBuff(x) @param spellID # returns true if player has buff x
-function br.playerHasBuff(spellID)
+function foodflaskpot:playerHasBuff(spellID)
local buffs, i = {}, 1
- local buff = br.UnitBuff("player", i)
+ local buff = br.functions.aura:UnitBuff("player", i)
while buff do
buffs[#buffs + 1] = buff
i = i + 1
- buff = select(10, br.UnitBuff("player", i))
+ buff = select(10, br.functions.aura:UnitBuff("player", i))
if buff ~= nil then
if buff == spellID then return true end
end
@@ -42,7 +45,7 @@ function br.playerHasBuff(spellID)
end
--getClass # returns class as string
-function br.getClass()
+function foodflaskpot:getClass()
local myClass = "nil"
local class = select(3, br._G.UnitClass("player"))
if class == 1 then -- Warrior
@@ -72,10 +75,10 @@ function br.getClass()
end
--getClassType # returns class type as string
-function br.getClassType()
+function foodflaskpot:getClassType()
local classType = "nil"
- local myClass = br.getClass()
- local mySpec = br._G.GetSpecialization()
+ local myClass = br.misc.foodflaskpot:getClass()
+ local mySpec = br._G.C_SpecializationInfo.GetSpecialization()
--Agility
if myClass == "Hunter"
or myClass == "Rogue"
@@ -109,7 +112,7 @@ function br.getClassType()
end
--playerHasItem(x) @param itemID # returns true if player has item x
-function br.playerHasItem(itemID)
+function foodflaskpot:playerHasItem(itemID)
if itemID == nil then return false end
local itemFound = false
for i = 0, 4 do --Let's look at each bag
@@ -127,10 +130,10 @@ function br.playerHasItem(itemID)
end
--playerCanUseItem(x) @param itemID # returns true if player can use item x
-function br.playerCanUseItem(itemID)
+function foodflaskpot:playerCanUseItem(itemID)
if itemID == nil then return false end
- if br.playerHasItem(itemID) then
- if br._G.GetItemCooldown(itemID) == 0 then
+ if br.misc.foodflaskpot:playerHasItem(itemID) then
+ if br._G.C_Container.GetItemCooldown(itemID) == 0 then
return true
end
end
@@ -138,38 +141,38 @@ function br.playerCanUseItem(itemID)
end
--usePotion(x) @param skipSurvivability # if x is true skips armor potion
-function br.usePotion(skipSurvivability)
+function foodflaskpot:usePotion(skipSurvivability)
if skipSurvivability == nil then skipSurvivability = false end
- local classType = br.getClassType()
+ local classType = br.misc.foodflaskpot:getClassType()
--Agility
if classType == "agilityClass" then
- if br.playerCanUseItem(agilityPotion) then
- if br.playerHasBuff(agilityPotionBuff) == false then
- br.useItem(agilityPotion)
+ if br.misc.foodflaskpot:playerCanUseItem(agilityPotion) then
+ if br.misc.foodflaskpot:playerHasBuff(agilityPotionBuff) == false then
+ br.functions.item:useItem(agilityPotion)
return true
end
end
--Intellect
elseif classType == "intellectClass" then
- if br.playerCanUseItem(intellectPotion) then
- if br.playerHasBuff(intellectPotionBuff) == false then
- br.useItem(intellectPotion)
+ if br.misc.foodflaskpot:playerCanUseItem(intellectPotion) then
+ if br.misc.foodflaskpot:playerHasBuff(intellectPotionBuff) == false then
+ br.functions.item:useItem(intellectPotion)
return true
end
end
--Strength
elseif classType == "strengthClass" or (classType == "staminaClass" and skipSurvivability == true) then
- if br.playerCanUseItem(strengthPotion) then
- if br.playerHasBuff(strengthPotionBuff) == false then
- br.useItem(strengthPotion)
+ if br.misc.foodflaskpot:playerCanUseItem(strengthPotion) then
+ if br.misc.foodflaskpot:playerHasBuff(strengthPotionBuff) == false then
+ br.functions.item:useItem(strengthPotion)
return true
end
end
--Armor
elseif classType == "staminaClass" and skipSurvivability == false then
- if br.playerCanUseItem(armorPotion) then
- if br.playerHasBuff(armorPotionBuff) == false then
- br.useItem(armorPotion)
+ if br.misc.foodflaskpot:playerCanUseItem(armorPotion) then
+ if br.misc.foodflaskpot:playerHasBuff(armorPotionBuff) == false then
+ br.functions.item:useItem(armorPotion)
return true
end
end
@@ -178,38 +181,38 @@ function br.usePotion(skipSurvivability)
end
--useFlask(x) @param skipSurvivability # if x is true skips stamina flask
-function br.useFlask(skipSurvivability)
+function foodflaskpot:useFlask(skipSurvivability)
if skipSurvivability == nil then skipSurvivability = false end
- local classType = br.getClassType()
+ local classType = br.misc.foodflaskpot:getClassType()
--Agility
if classType == "agilityClass" then
- if br.playerCanUseItem(agilityFlask) then
- if br.playerHasBuff(agilityFlaskBuff) == false then
- br.useItem(agilityFlask)
+ if br.misc.foodflaskpot:playerCanUseItem(agilityFlask) then
+ if br.misc.foodflaskpot:playerHasBuff(agilityFlaskBuff) == false then
+ br.functions.item:useItem(agilityFlask)
return true
end
end
--Intellect
elseif classType == "intellectClass" then
- if br.playerCanUseItem(intellectFlask) then
- if br.playerHasBuff(intellectFlaskBuff) == false then
- br.useItem(intellectFlask)
+ if br.misc.foodflaskpot:playerCanUseItem(intellectFlask) then
+ if br.misc.foodflaskpot:playerHasBuff(intellectFlaskBuff) == false then
+ br.functions.item:useItem(intellectFlask)
return true
end
end
--Strength
elseif classType == "strengthClass" or (classType == "staminaClass" and skipSurvivability == true) then
- if br.playerCanUseItem(strengthFlask) then
- if br.playerHasBuff(strengthFlaskBuff) == false then
- br.useItem(strengthFlask)
+ if br.misc.foodflaskpot:playerCanUseItem(strengthFlask) then
+ if br.misc.foodflaskpot:playerHasBuff(strengthFlaskBuff) == false then
+ br.functions.item:useItem(strengthFlask)
return true
end
end
--Stamina
elseif classType == "staminaClass" and skipSurvivability == false then
- if br.playerCanUseItem(staminaFlask) then
- if br.playerHasBuff(staminaFlaskBuff) == false then
- br.useItem(staminaFlask)
+ if br.misc.foodflaskpot:playerCanUseItem(staminaFlask) then
+ if br.misc.foodflaskpot:playerHasBuff(staminaFlaskBuff) == false then
+ br.functions.item:useItem(staminaFlask)
return true
end
end
@@ -218,38 +221,38 @@ function br.useFlask(skipSurvivability)
end
--useFood(x) @param skipSurvivability # if x is true skips stamina food
-function br.useFood(skipSurvivability)
+function foodflaskpot:useFood(skipSurvivability)
if skipSurvivability == nil then skipSurvivability = false end
- local classType = br.getClassType()
+ local classType = br.misc.foodflaskpot:getClassType()
--Agility
if classType == "agilityClass" then
- if br.playerCanUseItem(agilityFood) then
- if br.playerHasBuff(agilityFoodBuff) == false then
- br.useItem(agilityFood)
+ if br.misc.foodflaskpot:playerCanUseItem(agilityFood) then
+ if br.misc.foodflaskpot:playerHasBuff(agilityFoodBuff) == false then
+ br.functions.item:useItem(agilityFood)
return true
end
end
--Intellect
elseif classType == "intellectClass" then
- if br.playerCanUseItem(intellectFood) then
- if br.playerHasBuff(intellectFoodBuff) == false then
- br.useItem(intellectFood)
+ if br.misc.foodflaskpot:playerCanUseItem(intellectFood) then
+ if br.misc.foodflaskpot:playerHasBuff(intellectFoodBuff) == false then
+ br.functions.item:useItem(intellectFood)
return true
end
end
--Strength
elseif classType == "strengthClass" or (classType == "staminaClass" and skipSurvivability == true) then
- if br.playerCanUseItem(strengthFood) then
- if br.playerHasBuff(strengthFoodBuff) == false then
- br.useItem(strengthFood)
+ if br.misc.foodflaskpot:playerCanUseItem(strengthFood) then
+ if br.misc.foodflaskpot:playerHasBuff(strengthFoodBuff) == false then
+ br.functions.item:useItem(strengthFood)
return true
end
end
--Stamina
elseif classType == "staminaClass" and skipSurvivability == false then
- if br.playerCanUseItem(staminaFood) then
- if br.playerHasBuff(staminaFoodBuff) == false then
- br.useItem(staminaFood)
+ if br.misc.foodflaskpot:playerCanUseItem(staminaFood) then
+ if br.misc.foodflaskpot:playerHasBuff(staminaFoodBuff) == false then
+ br.functions.item:useItem(staminaFood)
return true
end
end
diff --git a/System/SlashCommands.lua b/System/SlashCommands.lua
index f19e2ee121..f658fb465e 100644
--- a/System/SlashCommands.lua
+++ b/System/SlashCommands.lua
@@ -1,16 +1,22 @@
local _, br = ...
+br.slashCommands = br.slashCommands or {}
+local slashCommands = br.slashCommands
+
-- Slash Commands
---------------------------------
function br._G.print(msg)
if msg == nil then
return
end
- local color = br.classColor or ""
- print(color .. "[BadRotations] |cffFFFFFF" .. msg)
+ local color = ""
+ if br.ui and br.ui.colors and br.ui.colors.class then
+ color = br.ui.colors.class
+ end
+ print(color .. "[BadRotations] |r" .. tostring(msg))
end
-br.commandHelp = { "|cffFF0000Slash Commands" }
-function br.SlashCommandHelp(cmd, msg)
+br.slashCommands.commandHelp = { "|cffFF0000Slash Commands" }
+function slashCommands:SlashCommandHelp(cmd, msg)
if cmd == nil then
cmd = ""
end
@@ -18,8 +24,8 @@ function br.SlashCommandHelp(cmd, msg)
msg = ""
end
if cmd == "Print Help" then
- for i = 1, #br.commandHelp do
- local command = br.commandHelp[i]
+ for i = 1, #br.slashCommands.commandHelp do
+ local command = br.slashCommands.commandHelp[i]
if i == 1 then
br._G.print(tostring(command))
else
@@ -28,19 +34,19 @@ function br.SlashCommandHelp(cmd, msg)
end
return
end
- table.insert(br.commandHelp, "|cffFFFFFF /" .. cmd .. "|cffFFDD11 - " .. msg)
- -- br.commandHelp = br.commandHelp .. "|cffFFFFFF\n /" .. cmd .. "|cffFFDD11 - " .. msg
+ table.insert(br.slashCommands.commandHelp, "|cffFFFFFF /" .. cmd .. "|cffFFDD11 - " .. msg)
+ -- br.slashCommands.commandHelp = br.slashCommands.commandHelp .. "|cffFFFFFF\n /" .. cmd .. "|cffFFDD11 - " .. msg
end
local function toggleUI()
local UIState = false
if UIState == false then
- br.mainButton:Click()
- br.BadRotationsButton:Show()
+ br.ui.toggles.mainButton:Click()
+ br.ui.minimapButton.frame:Show()
br.UIState = true
else
- br.mainButton:Click()
- br.BadRotationsButton:Hide()
+ br.ui.toggles.mainButton:Click()
+ br.ui.minimapButton.frame:Hide()
br.UIState = false
end
end
@@ -53,7 +59,7 @@ local function toggle(name, index, check)
if toggleFound == nil then
toggleFound = false
end
- for k, _ in pairs(br.data.settings[br.selectedSpec].toggles) do
+ for k, _ in pairs(br.data.settings[br.loader.selectedSpec].toggles) do
local toggle = string.upper(name) --name --(name:gsub("^%l", string.upper))
name = string.lower(name)
k = string.lower(k)
@@ -79,17 +85,17 @@ end
local function toggleRange(name, index1, index2)
if br.data == nil then return end
if br.data.settings == nil then return end
- if br.data.settings[br.selectedSpec] == nil then return end
- if br.data.settings[br.selectedSpec].toggles == nil then return end
- for k, _ in pairs(br.data.settings[br.selectedSpec].toggles) do
+ if br.data.settings[br.loader.selectedSpec] == nil then return end
+ if br.data.settings[br.loader.selectedSpec].toggles == nil then return end
+ for k, _ in pairs(br.data.settings[br.loader.selectedSpec].toggles) do
local toggle = string.upper(name) --name --(name:gsub("^%l", string.upper))
name = string.lower(name)
local lowerKey = string.lower(k)
if name == lowerKey then
- if tostring(br.data.settings[br.selectedSpec].toggles[k]) == index1 then
+ if tostring(br.data.settings[br.loader.selectedSpec].toggles[k]) == index1 then
br.ToggleToValue(toggle, index2)
return
- elseif tostring(br.data.settings[br.selectedSpec].toggles[k]) == index2 then
+ elseif tostring(br.data.settings[br.loader.selectedSpec].toggles[k]) == index2 then
br.ToggleToValue(toggle, index1)
return
else
@@ -114,7 +120,7 @@ local function getStringIndex(string, index)
end
local function updateRate()
- print("Current Update Rate: " .. br:getUpdateRate())
+ print("Current Update Rate: " .. br.engines:getUpdateRate())
end
local function forewardDisengage() -- from Stinky Twitch
@@ -127,30 +133,31 @@ local function forewardDisengage() -- from Stinky Twitch
end
end
-function br:slashHelpList()
+function slashCommands:slashHelpList()
SLASH_BR1, SLASH_BR2 = "/br", "/badrotations"
- br.SlashCommandHelp("br", "Toggles BadRotations On/Off")
- br.SlashCommandHelp("br help", "Displays this list of help commands. ***Obviously***")
- br.SlashCommandHelp("br cooldowns", "Toggles CDs between on/off")
- br.SlashCommandHelp("br blacklist mouseover", "Adds/Removes mouseover unit to healing blacklist.")
- br.SlashCommandHelp("br blacklist dump", "Prints all units currently on blacklist.")
- br.SlashCommandHelp("br blacklist clear", "Clears the blacklist.")
+ slashCommands:SlashCommandHelp("br", "Toggles BadRotations On/Off")
+ slashCommands:SlashCommandHelp("br help", "Displays this list of help commands. ***Obviously***")
+ slashCommands:SlashCommandHelp("br cooldowns", "Toggles CDs between on/off")
+ slashCommands:SlashCommandHelp("br blacklist mouseover", "Adds/Removes mouseover unit to healing blacklist.")
+ slashCommands:SlashCommandHelp("br blacklist dump", "Prints all units currently on blacklist.")
+ slashCommands:SlashCommandHelp("br blacklist clear", "Clears the blacklist.")
-- SlashCommandHelp("br pause hold","Pauses while held (via macro).")
- br.SlashCommandHelp("br pause toggle", "Switches Pause On/Off")
- br.SlashCommandHelp("br queue clear", "Clears the Spell Queue of all queued spells.")
- br.SlashCommandHelp("br queue add spellId", "Adds the Spell to the Queue by Spell Id.")
- br.SlashCommandHelp("br queue remove spellId", "Removes the Spell from the Queue by Spell Id.")
- br.SlashCommandHelp("br ui", "Shows a list of toggleable UI elements, IE: /br ui main")
- br.SlashCommandHelp("br updaterate", "Displays Current Update Rate")
+ slashCommands:SlashCommandHelp("br pause toggle", "Switches Pause On/Off")
+ slashCommands:SlashCommandHelp("br queue clear", "Clears the Spell Queue of all queued spells.")
+ slashCommands:SlashCommandHelp("br queue add spellId", "Adds the Spell to the Queue by Spell Id.")
+ slashCommands:SlashCommandHelp("br queue remove spellId", "Removes the Spell from the Queue by Spell Id.")
+ slashCommands:SlashCommandHelp("br ui", "Shows a list of toggleable UI elements, IE: /br ui main")
+ slashCommands:SlashCommandHelp("br updaterate", "Displays Current Update Rate")
if select(2, br._G.UnitClass("player")) == "HUNTER" then
- br.SlashCommandHelp("br disengage", "Assign to macro to Forward Disengage.")
+ slashCommands:SlashCommandHelp("br disengage", "Assign to macro to Forward Disengage.")
end
- br.SlashCommandHelp("br healing", "Adds/Removes Target from Healing List")
- br.SlashCommandHelp("br baddebuff spellId", "Adds/Removes Debuff from Do Not Heal List")
+ slashCommands:SlashCommandHelp("br healing", "Adds/Removes Target from Healing List")
+ slashCommands:SlashCommandHelp("br baddebuff spellId", "Adds/Removes Debuff from Do Not Heal List")
end
-br:slashHelpList()
-function br.handler(message, editbox)
+slashCommands:slashHelpList()
+
+local function handler(message, editbox)
local msg = string.lower(message)
local msg1 = getStringIndex(message, 1)
local msg2 = getStringIndex(message, 2)
@@ -196,7 +203,7 @@ function br.handler(message, editbox)
if not br.data.blackList then
br.data.blackList = {}
end
- if br.GetUnitExists("mouseover") then
+ if br.functions.unit:GetUnitExists("mouseover") then
local mouseoverName = br._G.UnitName("mouseover")
local mouseoverGUID = br._G.UnitGUID("mouseover")
-- Now we're trying to find that unit in the blackList table to remove
@@ -222,18 +229,18 @@ function br.handler(message, editbox)
elseif msg1 == "pause" then
-- Pause
--[[if msg2 == "hold" then
- br.ChatOverlay("Profile Paused")
+ br.ui.chatOverlay:Show("Profile Paused")
return true
else]]
if msg2 == "toggle" then
- if br.data.settings[br.selectedSpec].toggles["Pause"] == 0 then
- br.ChatOverlay("\124cFFED0000 -- Paused -- ")
+ if br.data.settings[br.loader.selectedSpec].toggles["Pause"] == 0 then
+ br.ui.chatOverlay:Show("\124cFFED0000 -- Paused -- ")
br._G.print("|cFFFF0000Paused")
- br.data.settings[br.selectedSpec].toggles["Pause"] = 1
+ br.data.settings[br.loader.selectedSpec].toggles["Pause"] = 1
else
- br.ChatOverlay("\124cFF3BB0FF -- Pause Removed -- ")
+ br.ui.chatOverlay:Show("\124cFF3BB0FF -- Pause Removed -- ")
br._G.print("|cFF3BB0FFPause Removed")
- br.data.settings[br.selectedSpec].toggles["Pause"] = 0
+ br.data.settings[br.loader.selectedSpec].toggles["Pause"] = 0
end
else
br._G.print(
@@ -284,10 +291,10 @@ function br.handler(message, editbox)
'This will cast spell 123456 on "player" as an AOE once there are at least 3 units within 8 yards of the player.\n'
)
else
- local spellName, _, _, _, _, _, spellId = br._G.GetSpellInfo(msg3)
+ local spellName, _, _, _, _, _, spellId = br.api.wow.GetSpellInfo(msg3)
local notOnCD = true
if br ~= nil and br.player ~= nil and spellName ~= nil then
- notOnCD = br.getSpellCD(spellName) <= br.player.gcdMax
+ notOnCD = br.functions.spell:getSpellCD(spellName) <= br.player.gcdMax
end
if msg4 ~= nil then
targetUnit = tostring(msg4)
@@ -347,7 +354,7 @@ function br.handler(message, editbox)
if msg3 == nil then
br._G.print("No Spell Provided to remove from Queue.")
else
- local spellName, _, _, _, _, _, spellId = br._G.GetSpellInfo(msg3)
+ local spellName, _, _, _, _, _, spellId = br.api.wow.GetSpellInfo(msg3)
local removedSpell = false
if #br.player.queue ~= 0 then
for i = 1, #br.player.queue do
@@ -380,22 +387,22 @@ function br.handler(message, editbox)
elseif msg1 == "healing" then
-- Special Heal List
local unit
- if br.GetUnitExists("mouseover") then
+ if br.functions.unit:GetUnitExists("mouseover") then
unit = "mouseover"
- elseif br.GetUnitExists("focus") then
+ elseif br.functions.unit:GetUnitExists("focus") then
unit = "focus"
- elseif br.GetUnitExists("target") then
+ elseif br.functions.unit:GetUnitExists("target") then
unit = "target"
end
if unit then
local unitName = br._G.UnitName(unit)
local unitGUID = br._G.UnitGUID(unit)
- if br.novaEngineTables.SpecialHealUnitList[unitGUID] ~= nil then
- br.novaEngineTables.SpecialHealUnitList[unitGUID] = nil
+ if br.engines.healingEngineCollections.SpecialHealUnitList[unitGUID] ~= nil then
+ br.engines.healingEngineCollections.SpecialHealUnitList[unitGUID] = nil
br._G.print("|cffFFDD11" .. unitName .. "|cffFF0000 Removed from Special Heal List")
else
br._G.print("|cffFFDD11" .. unitName .. "|cffFF0000 Added to Special Heal List")
- br.novaEngineTables.SpecialHealUnitList[unitGUID] = unitName
+ br.engines.healingEngineCollections.SpecialHealUnitList[unitGUID] = unitName
end
else
br._G.print("No Target Found. Please Select a Target Before Adding")
@@ -405,23 +412,23 @@ function br.handler(message, editbox)
if msg2 == nil then
br._G.print("No Spell Provided to Add")
else
- local spellName, _, _, _, _, _, spellId = br._G.GetSpellInfo(msg3)
+ local spellName, _, _, _, _, _, spellId = br.api.wow.GetSpellInfo(msg3)
if spellName == nil then
br._G.print("Invalid Spell ID: |cffFFDD11 Unable to add.")
else
- if br.novaEngineTables.BadDebuffList[spellId] ~= nil then
- br.novaEngineTables.BadDebuffList[spellId] = nil
+ if br.engines.healingEngineCollections.BadDebuffList[spellId] ~= nil then
+ br.engines.healingEngineCollections.BadDebuffList[spellId] = nil
br._G.print("|cffFFDD11" .. spellName .. "|cffFF0000 Removed from Debuff List")
else
br._G.print("|cffFFDD11" .. spellName .. "|cffFF0000 Added to Debuff List")
- br.novaEngineTables.BadDebuffList[spellId] = spellName
+ br.engines.healingEngineCollections.BadDebuffList[spellId] = spellName
end
end
end
elseif msg == "disengage" then
-- Forward Disengage
if br._G.IsLeftShiftKeyDown() then
- br._G.CastSpellByName(br._G.GetSpellInfo(781))
+ br._G.CastSpellByName(br.api.wow.GetSpellInfo(781))
else
forewardDisengage()
end
@@ -440,20 +447,20 @@ function br.handler(message, editbox)
br._G.print(
"Combat Lockdown detected. Unable to modify button bar. Please try again when out of combat.")
else
- if br.data.settings[br.selectedSpec].toggles["Main"] == 1 then
- br.data.settings[br.selectedSpec].toggles["Main"] = 0
- br.mainButton:Hide()
+ if br.data.settings[br.loader.selectedSpec].toggles["Main"] == 1 then
+ br.data.settings[br.loader.selectedSpec].toggles["Main"] = 0
+ br.ui.toggles.mainButton:Hide()
else
- br.data.settings[br.selectedSpec].toggles["Main"] = 1
- br.mainButton:Show()
+ br.data.settings[br.loader.selectedSpec].toggles["Main"] = 1
+ br.ui.toggles.mainButton:Show()
end
end
elseif msg2 == "icon" then
if br.hiddenIcon == nil or br.hiddenIcon == false then
- br.BadRotationsButton:Hide()
+ br.ui.minimapButton.frame:Hide()
br.hiddenIcon = true
else
- br.BadRotationsButton:Show()
+ br.ui.minimapButton.frame:Show()
br.hiddenIcon = false
end
elseif msg2 == nil then
@@ -468,27 +475,32 @@ function br.handler(message, editbox)
end
elseif msg1 == "update" then
br.updater:Update()
+ elseif msg1 == "clean" or msg1 == "cleansettings" then
+ if br.ui and br.ui.settingsManagement then
+ br._G.print("|cffFFDD11[BadRotations]|r Manually cleaning orphaned settings...")
+ br.ui.settingsManagement:cleanSettings()
+ else
+ br._G.print("|cffFF0000[BadRotations]|r Settings management not available")
+ end
else
br._G.print("Invalid Command: |cFFFF0000" .. msg .. "|r try |cffFFDD11 /br help")
end
end
-br._G.SlashCmdList["BR"] = br.handler
-
-- macro used to gather caster/spell/buff on our actual target
SLASH_dumpInfo1 = "/dumpinfo"
-function br._G.SlashCmdList.dumpInfo(msg, editbox)
+local function dumpInfo(msg, editbox)
-- find unit in our engines
- for k, _ in pairs(br.enemy) do
- if br.enemy[k].guid == br._G.UnitGUID("target") then
+ for k, _ in pairs(br.engines.enemiesEngine.enemy) do
+ if br.engines.enemiesEngine.enemy[k].guid == br._G.UnitGUID("target") then
_G.targetInfo = {}
_G.targetInfo.name = br._G.UnitName("target")
- local thisUnit = br.enemy[k].unit
+ local thisUnit = br.engines.enemiesEngine.enemy[k].unit
_G.targetInfo.unitID = thisUnit.id
- local buff1 = br.UnitBuff("target", 1)
- local buff2 = br.UnitBuff("target", 2)
- local deBuff1 = br.UnitBuff("target", 1)
- local deBuff2 = br.UnitBuff("target", 2)
+ local buff1 = br.functions.aura:UnitBuff("target", 1)
+ local buff2 = br.functions.aura:UnitBuff("target", 2)
+ local deBuff1 = br.functions.aura:UnitBuff("target", 1)
+ local deBuff2 = br.functions.aura:UnitBuff("target", 2)
if buff1 then
_G.targetInfo.buff1 = buff1
end
@@ -507,3 +519,6 @@ function br._G.SlashCmdList.dumpInfo(msg, editbox)
end
end
end
+
+br._G.SlashCmdList["BR"] = handler
+br._G.SlashCmdList.dumpInfo = dumpInfo
\ No newline at end of file
diff --git a/System/UI/ChatOverlay.lua b/System/UI/ChatOverlay.lua
index 64658f31ba..a4fbfdedf6 100644
--- a/System/UI/ChatOverlay.lua
+++ b/System/UI/ChatOverlay.lua
@@ -1,4 +1,7 @@
local _, br = ...
+br.ui.chatOverlay = br.ui.chatOverlay or {}
+local chatOverlay = br.ui.chatOverlay
+
-- Chat Overlay: Originally written by Sheuron.
local function onUpdate(self)
if self.time < br._G.GetTime() - 2.0 then
@@ -9,27 +12,31 @@ local function onUpdate(self)
end
end
end
-br.chatOverlayText = br._G.CreateFrame("Frame", nil, br._G.ChatFrame1)
-br.chatOverlayText:SetSize(br._G.ChatFrame1:GetWidth(), 50)
-br.chatOverlayText:Hide()
-br.chatOverlayText:SetScript("OnUpdate", onUpdate)
-br.chatOverlayText:SetPoint("TOP", 0, 0)
-br.chatOverlayText.text = br.chatOverlayText:CreateFontString(nil, "OVERLAY", "MovieSubtitleFont")
-br.chatOverlayText.text:SetAllPoints()
-br.chatOverlayText.texture = br.chatOverlayText:CreateTexture()
-br.chatOverlayText.texture:SetAllPoints()
-br.chatOverlayText.texture:SetTexture(0, 0, 0, .50)
-br.chatOverlayText.time = 0
-function br.ChatOverlay(Message, FadingTime)
- if br.getOptionCheck("Overlay Messages") then
- br.chatOverlayText:SetSize(br._G.ChatFrame1:GetWidth(), 50)
- br.chatOverlayText.text:SetText(Message)
- br.chatOverlayText:SetAlpha(1)
+
+-- Chat Overlay Frame
+chatOverlay.frame = br._G.CreateFrame("Frame", nil, br._G.ChatFrame1)
+chatOverlay.frame:SetSize(br._G.ChatFrame1:GetWidth(), 50)
+chatOverlay.frame:Hide()
+chatOverlay.frame:SetScript("OnUpdate", onUpdate)
+chatOverlay.frame:SetPoint("TOP", 0, 0)
+chatOverlay.frame.text = chatOverlay.frame:CreateFontString(nil, "OVERLAY", "MovieSubtitleFont")
+chatOverlay.frame.text:SetAllPoints()
+chatOverlay.frame.texture = chatOverlay.frame:CreateTexture()
+chatOverlay.frame.texture:SetAllPoints()
+chatOverlay.frame.texture:SetTexture(0, 0, 0, .50)
+chatOverlay.frame.time = 0
+
+-- Function to show messages in chat overlay
+function chatOverlay:Show(Message, FadingTime)
+ if br.functions.misc:getOptionCheck("Overlay Messages") then
+ chatOverlay.frame:SetSize(br._G.ChatFrame1:GetWidth(), 50)
+ chatOverlay.frame.text:SetText(Message)
+ chatOverlay.frame:SetAlpha(1)
if FadingTime == nil or type(FadingTime) ~= "number" then
- br.chatOverlayText.time = br._G.GetTime()
+ chatOverlay.frame.time = br._G.GetTime()
else
- br.chatOverlayText.time = br._G.GetTime() - 2 + FadingTime
+ chatOverlay.frame.time = br._G.GetTime() - 2 + FadingTime
end
- br.chatOverlayText:Show()
+ chatOverlay.frame:Show()
end
end
diff --git a/System/UI/Colors.lua b/System/UI/Colors.lua
new file mode 100644
index 0000000000..561a9e935d
--- /dev/null
+++ b/System/UI/Colors.lua
@@ -0,0 +1,34 @@
+local _, br = ...
+br.ui.colors = br.ui.colors or {}
+local colors = br.ui.colors
+
+-- [[ Class Colors Definition ]] --
+-- The colors Duke, the colors!
+local classColors = {
+ [1] = { class = "Warrior", B = 0.43, G = 0.61, R = 0.78, hex = "c79c6e" },
+ [2] = { class = "Paladin", B = 0.73, G = 0.55, R = 0.96, hex = "f58cba" },
+ [3] = { class = "Hunter", B = 0.45, G = 0.83, R = 0.67, hex = "abd473" },
+ [4] = { class = "Rogue", B = 0.41, G = 0.96, R = 1, hex = "fff569" },
+ [5] = { class = "Priest", B = 1, G = 1, R = 1, hex = "ffffff" },
+ [6] = { class = "Deathknight", B = 0.23, G = 0.12, R = 0.77, hex = "c41f3b" },
+ [7] = { class = "Shaman", B = 0.87, G = 0.44, R = 0, hex = "0070de" },
+ [8] = { class = "Mage", B = 0.94, G = 0.8, R = 0.41, hex = "69ccf0" },
+ [9] = { class = "Warlock", B = 0.79, G = 0.51, R = 0.58, hex = "9482c9" },
+ [10] = { class = "Monk", B = 0.59, G = 1, R = 0, hex = "00ff96" },
+ [11] = { class = "Druid", B = 0.04, G = 0.49, R = 1, hex = "ff7d0a" },
+ [12] = { class = "Demonhunter", B = 0.79, G = 0.19, R = 0.64, hex = "a330c9" },
+ [13] = { class = "Evoker", B = 0.50, G = 0.58, R = 0.20, hex = "33937f" }
+}
+
+colors.quality = {
+ blue = "0070dd",
+ green = "1eff00",
+ white = "ffffff",
+ grey = "9d9d9d"
+}
+
+function colors:getColor(unit)
+ if unit == nil then unit = "player" end
+ return classColors[select(3, br._G.UnitClass(unit))] or { class = "Unknown", B = 1, G = 1, R = 1, hex = "ffffff" }
+end
+colors.class = tostring("|cff" .. colors:getColor("player").hex)
\ No newline at end of file
diff --git a/System/UI/DropOptions.lua b/System/UI/DropOptions.lua
new file mode 100644
index 0000000000..ba1007bcae
--- /dev/null
+++ b/System/UI/DropOptions.lua
@@ -0,0 +1,17 @@
+local _, br = ...
+br.ui.dropOptions = br.ui.dropOptions or {}
+local dropOptions = br.ui.dropOptions
+
+dropOptions.Toggle = {
+ "LeftCtrl",
+ "LeftShift",
+ "RightCtrl",
+ "RightShift",
+ "RightAlt",
+ "None",
+ "MMouse",
+ "Mouse4",
+ "Mouse5"
+}
+dropOptions.AlwaysCdNever = { "|cff00FF00Always", "|cffFFFF00Cooldowns", "|cffFF0000Never" }
+dropOptions.AlwaysCdAoeNever = { "|cff00FF00Always", "|cff008000AOE", "|cffffff00AOE/CD", "|cff0000ffCD", "|cffff0000Never" }
\ No newline at end of file
diff --git a/System/UI/Elements/Arrow.lua b/System/UI/Elements/Arrow.lua
index 19c4de1436..89584df2f4 100644
--- a/System/UI/Elements/Arrow.lua
+++ b/System/UI/Elements/Arrow.lua
@@ -33,7 +33,7 @@ function br.ui:createRightArrow(window)
function()
local page = window.currentPage
if page then
- -- br.data.settings[br.selectedSpec][window] = page
+ -- br.data.settings[br.loader.selectedSpec][window] = page
if page + 1 > #window.pages then
window.pageDD:SetValue(1)
else
@@ -79,7 +79,7 @@ function br.ui:createLeftArrow(window)
function()
local page = window.currentPage
if page then
- -- br.data.settings[br.selectedSpec][window] = page
+ -- br.data.settings[br.loader.selectedSpec][window] = page
if page - 1 == 0 then
window.pageDD:SetValue(#window.pages)
else
diff --git a/System/UI/Elements/Button.lua b/System/UI/Elements/Button.lua
index ac024f3418..213c2f2ea7 100644
--- a/System/UI/Elements/Button.lua
+++ b/System/UI/Elements/Button.lua
@@ -81,7 +81,7 @@ function br.ui:createSaveButton(parent, buttonName, x, y)
"OnClick",
function()
local profiles =
- br.fetch(br.selectedSpec .. "_" .. "profiles", { { key = "default", text = "Default" } })
+ br.ui.settingsManagement:fetch(br.loader.selectedSpec .. "_" .. "profiles", { { key = "default", text = "Default" } })
local profileName = profileInput:GetText()
local pkey = string.gsub(profileName, "%s+", "")
if profileName ~= "" then
@@ -98,9 +98,9 @@ function br.ui:createSaveButton(parent, buttonName, x, y)
return false
end
end
- table.insert(profiles, { key = pkey, text = br.deepcopy(br.data.settings[br.selectedSpec]) })
- br.store(br.selectedSpec .. "_" .. "profiles", profiles)
- br.store(br.selectedSpec .. "_" .. "profile", pkey)
+ table.insert(profiles, { key = pkey, text = br.ui.settingsManagement:deepcopy(br.data.settings[br.loader.selectedSpec]) })
+ br.ui.settingsManagement:store(br.loader.selectedSpec .. "_" .. "profiles", profiles)
+ br.ui.settingsManagement:store(br.loader.selectedSpec .. "_" .. "profile", pkey)
newWindow:Hide()
parent:Hide()
parent:Release()
@@ -112,7 +112,7 @@ function br.ui:createSaveButton(parent, buttonName, x, y)
"OnEnterPressed",
function()
local profiles =
- br.fetch(br.selectedSpec .. "_" .. "profiles", { { key = "default", text = "Default" } })
+ br.ui.settingsManagement:fetch(br.loader.selectedSpec .. "_" .. "profiles", { { key = "default", text = "Default" } })
local profileName = profileInput:GetText()
local pkey = string.gsub(profileName, "%s+", "")
if profileName ~= "" then
@@ -129,9 +129,9 @@ function br.ui:createSaveButton(parent, buttonName, x, y)
return false
end
end
- table.insert(profiles, { key = pkey, text = br.deepcopy(br.data.settings[br.selectedSpec]) })
- br.store(br.selectedSpec .. "_" .. "profiles", profiles)
- br.store(br.selectedSpec .. "_" .. "profile", pkey)
+ table.insert(profiles, { key = pkey, text = br.ui.settingsManagement:deepcopy(br.data.settings[br.loader.selectedSpec]) })
+ br.ui.settingsManagement:store(br.loader.selectedSpec .. "_" .. "profiles", profiles)
+ br.ui.settingsManagement:store(br.loader.selectedSpec .. "_" .. "profile", pkey)
newWindow:Hide()
parent:Hide()
parent:Release()
@@ -162,14 +162,14 @@ function br.ui:createDeleteButton(parent, buttonName, x, y)
"OnClick",
function()
local selectedProfile = br.profileDropValue
- local profiles = br.fetch(br.selectedSpec .. "_" .. "profiles", { { key = "default", text = "Default" } })
+ local profiles = br.ui.settingsManagement:fetch(br.loader.selectedSpec .. "_" .. "profiles", { { key = "default", text = "Default" } })
if selectedProfile ~= "default" then
for i, p in ipairs(profiles) do
if p.key == selectedProfile then
profiles[i] = nil
br._G.print("Deleting " .. selectedProfile .. " profile!")
- br.store(br.selectedSpec .. "_" .. "profiles", profiles)
- br.store(br.selectedSpec .. "_" .. "profile", "default")
+ br.ui.settingsManagement:store(br.loader.selectedSpec .. "_" .. "profiles", profiles)
+ br.ui.settingsManagement:store(br.loader.selectedSpec .. "_" .. "profile", "default")
parent:Hide()
parent:Release()
br.rotationChanged = true
@@ -204,12 +204,12 @@ function br.ui:createLoadButton(parent, buttonName, x, y)
br._G.print("Please select a profile other than default!")
return
end
- if br.profile ~= nil then
- if br.profile[br.selectedSpec .. "_" .. "profiles"] ~= nil then
- local lProfile = br.profile[br.selectedSpec .. "_" .. "profiles"]
+ if br.ui.settingsManagement.profile ~= nil then
+ if br.ui.settingsManagement.profile[br.loader.selectedSpec .. "_" .. "profiles"] ~= nil then
+ local lProfile = br.ui.settingsManagement.profile[br.loader.selectedSpec .. "_" .. "profiles"]
for _, v in pairs(lProfile) do
if v.key == br.profileDropValue then
- br.data.settings[br.selectedSpec] = br.deepcopy(v.text)
+ br.data.settings[br.loader.selectedSpec] = br.ui.settingsManagement:deepcopy(v.text)
br._G.print(v.key .. " profile found!")
br.rotationChanged = true
break
@@ -240,14 +240,14 @@ function br.ui:createExportButton(parent, buttonName, x, y)
exportButton:SetEventListener(
"OnClick",
function()
- br:cleanSettings()
- br:saveSettings("Exported Settings", nil, br.selectedSpec, br.selectedProfileName)
- -- br:saveSettings(nil,"Exported Settings")
+ br.ui.settingsManagement:cleanSettings()
+ br.ui.settingsManagement:saveSettings("Exported Settings", nil, br.loader.selectedSpec, br.loader.selectedProfileName)
+ -- br.ui.settingsManagement:saveSettings(nil,"Exported Settings")
-- -- Save br.data for current profile to Settings folder
- -- local exportDir = br:checkDirectories("Exported Settings")
+ -- local exportDir = br.ui.settingsManagement:checkDirectories("Exported Settings")
-- local savedSettings = deepcopy(br.data)
- -- local settingsFile = br.selectedSpec..selectedProfileName
+ -- local settingsFile = br.loader.selectedSpec..selectedProfileName
-- if savedSettings ~= nil then
-- br.tableSave(savedSettings,exportDir .. settingsFile .. ".lua")
-- Print("Exported Settings for Profile "..settingsFile.." to Exported Settings Folder")
@@ -276,36 +276,36 @@ function br.ui:createImportButton(parent, buttonName, x, y)
"OnClick",
function()
br.data.loadedSettings = false
- br:loadSettings("Exported Settings", nil, br.selectedSpec, br.selectedProfileName)
+ br.ui.settingsManagement:loadSettings("Exported Settings", nil, br.loader.selectedSpec, br.loader.selectedProfileName)
br._G.ReloadUI()
-- -- Load settings file matching profile to br.data
- -- local loadDir = br:checkDirectories("Exported Settings")
+ -- local loadDir = br.ui.settingsManagement:checkDirectories("Exported Settings")
-- local brdata
-- local brprofile
-- local fileFound = false
-- local profileFound = false
-- -- Load Settings
- -- if br:findFileInFolder(br.settingsFile,loadDir) then
- -- Print("Loading Settings File: " .. br.settingsFile)
- -- brdata = br.tableLoad(loadDir .. br.settingsFile)
+ -- if br.ui.settingsManagement:findFileInFolder(br.loader.settingsFile,loadDir) then
+ -- Print("Loading Settings File: " .. br.loader.settingsFile)
+ -- brdata = br.tableLoad(loadDir .. br.loader.settingsFile)
-- fileFound = true
-- end
-- -- Load Profile
- -- if br:findFileInFolder("savedProfile.lua",loadDir) then
- -- brprofile = br.tableLoad(loadDir .. br.settingsFile)
+ -- if br.ui.settingsManagement:findFileInFolder("savedProfile.lua",loadDir) then
+ -- brprofile = br.tableLoad(loadDir .. br.loader.settingsFile)
-- profileFound = true
-- end
-- if fileFound then
-- br.ui:closeWindow("all")
- -- br.mainButton:Hide()
+ -- br.ui.toggles.mainButton:Hide()
-- br.data = deepcopy(brdata)
- -- if profileFound then br.profile = deepcopy(brprofile) end
+ -- if profileFound then br.ui.settingsManagement.profile = deepcopy(brprofile) end
-- br.ui:recreateWindows()
- -- Print("Loaded Settings for Profile "..br.selectedProfileName..", from Exported Settings Folder")
+ -- Print("Loaded Settings for Profile "..br.loader.selectedProfileName..", from Exported Settings Folder")
-- ReloadUI()
-- else
-- Print("Error Loading Settings File")
- -- if not fileFound then Print("No File Called "..br.settingsFile.." Found In "..loadDir) end
+ -- if not fileFound then Print("No File Called "..br.loader.settingsFile.." Found In "..loadDir) end
-- end
end
)
@@ -313,145 +313,4 @@ function br.ui:createImportButton(parent, buttonName, x, y)
parent:AddChild(importButton)
return importButton
-end
-
---------------------------------------------------------------------------
--- Table Save/Load code from: http://lua-users.org/wiki/SaveTableToFile --
---------------------------------------------------------------------------
-local function exportstring(s)
- return string.format("%q", s)
-end
-
---// The Save Function
-br.tableSave = function(tbl, filename)
- local charS, charE = " ", "\n"
- -- local file,err = io.open( filename, "wb" )
- -- if err then return err end
- -- br._G.WriteFile(filename, "", false)
- local content = ""
- -- initiate variables for save procedure
- local tables, lookup = { tbl }, { [tbl] = 1 }
- -- file:write( "return {"..charE )
- -- br._G.WriteFile(filename, "return {" .. charE, true)
- content = content .. "return {" .. charE
-
- for idx, t in ipairs(tables) do
- -- file:write( "-- Table: {"..idx.."}"..charE )
- -- br._G.WriteFile(filename, "-- Table: {" .. idx .. "}" .. charE, true)
- content = content .. "-- Table: {" .. idx .. "}" .. charE
- -- file:write( "{"..charE )
- -- br._G.WriteFile(filename, "{" .. charE, true)
- content = content .. "{" .. charE
- local thandled = {}
-
- for i, v in ipairs(t) do
- thandled[i] = true
- local stype = type(v)
- -- only handle value
- if stype == "table" then
- if not lookup[v] then
- table.insert(tables, v)
- lookup[v] = #tables
- end
- -- file:write( charS.."{"..lookup[v].."},"..charE )
- -- br._G.WriteFile(filename, charS .. "{" .. lookup[v] .. "}," .. charE, true)
- content = content .. charS .. "{" .. lookup[v] .. "}," .. charE
- elseif stype == "string" then
- -- file:write( charS..exportstring( v )..","..charE )
- -- br._G.WriteFile(filename, charS .. exportstring(v) .. "," .. charE, true)
- content = content .. charS .. exportstring(v) .. "," .. charE
- elseif stype == "number" or stype == "boolean" then
- -- file:write( charS..tostring( v )..","..charE )
- -- br._G.WriteFile(filename, charS .. tostring(v) .. "," .. charE, true)
- content = content .. charS .. tostring(v) .. "," .. charE
- end
- end
-
- for i, v in pairs(t) do
- -- escape handled values
- if (not thandled[i]) then
- local str = ""
- local stype = type(i)
- -- handle index
- if stype == "table" then
- if not lookup[i] then
- table.insert(tables, i)
- lookup[i] = #tables
- end
- str = charS .. "[{" .. lookup[i] .. "}]="
- elseif stype == "string" then
- str = charS .. "[" .. exportstring(i) .. "]="
- elseif stype == "number" then
- str = charS .. "[" .. tostring(i) .. "]="
- elseif stype == "boolean" then
- str = charS .. "[" .. i .. "]="
- end
-
- if str ~= "" then
- stype = type(v)
- -- handle value
- if stype == "table" then
- if not lookup[v] then
- table.insert(tables, v)
- lookup[v] = #tables
- end
- -- file:write( str.."{"..lookup[v].."},"..charE )
- -- br._G.WriteFile(filename, str .. "{" .. lookup[v] .. "}," .. charE, true)
- content = content .. str .. "{" .. lookup[v] .. "}," .. charE
- elseif stype == "string" then
- -- file:write( str..exportstring( v )..","..charE )
- -- br._G.WriteFile(filename, str .. exportstring(v) .. "," .. charE, true)
- content = content .. str .. exportstring(v) .. "," .. charE
- elseif stype == "number" or stype == "boolean" then
- -- file:write( str..tostring( v )..","..charE )
- -- br._G.WriteFile(filename, str .. tostring(v) .. "," .. charE, true)
- content = content .. str .. tostring(v) .. "," .. charE
- end
- end
- end
- end
- -- file:write( "},"..charE )
- -- br._G.WriteFile(filename, "}," .. charE, true)
- content = content .. "}," .. charE
- end
- -- file:write( "}" )
- -- br._G.WriteFile(filename, "}", true)
- content = content .. "}"
- -- file:close()
- br._G.WriteFile(filename, content, false)
-end
-
---// The Load Function
-br.tableLoad = function(sfile)
- local file = br._G.ReadFile(sfile)
- if file == nil or file == "" then
- return
- end
- local ftables = br._G.loadstring(file, sfile)
- -- local ftables,err = loadfile( sfile )
- -- if err then return _,err end
- local tables
- if (ftables) then
- tables = ftables()
- else
- tables = {}
- end
- -- return deepcopy(tables)
- for idx = 1, #tables do
- local tolinki = {}
- for i, v in pairs(tables[idx]) do
- if type(v) == "table" then
- tables[idx][i] = tables[v[1]]
- end
- if type(i) == "table" and tables[i[1]] then
- table.insert(tolinki, { i, tables[i[1]] })
- end
- end
- -- link indices
- for _, v in ipairs(tolinki) do
- tables[idx][v[2]], tables[idx][v[1]] = tables[idx][v[1]], nil
- end
- end
- br.loadFile = true
- return tables[1]
-end
+end
\ No newline at end of file
diff --git a/System/UI/Elements/Checkbox.lua b/System/UI/Elements/Checkbox.lua
index 128687365e..3a52aadacc 100644
--- a/System/UI/Elements/Checkbox.lua
+++ b/System/UI/Elements/Checkbox.lua
@@ -5,7 +5,7 @@ local _, br = ...
function br.ui:createCheckbox(parent, text, tooltip, checked)
-- Class Specific Color for UI Elements
local classColor = {
- color = br.classColors[select(3, br._G.UnitClass("player"))].hex
+ color = br.ui.colors:getColor("player").hex
}
-- Option Storage Location
-- local parent1 = parent.children[1]
@@ -47,11 +47,31 @@ function br.ui:createCheckbox(parent, text, tooltip, checked)
--------------
---BR Stuff---
--------------
- local activePageIdx = parent.settings.parentObject.pageDD.value
- local activePage = parent.settings.parentObject.pageDD.settings.list[activePageIdx]
- br.data.settings[br.selectedSpec][br.selectedProfile][activePage] = br.data.settings[br.selectedSpec]
- [br.selectedProfile][activePage] or {}
- local data = br.data.settings[br.selectedSpec][br.selectedProfile][activePage]
+ local activePage = parent.settings.sectionName or ""
+ -- Fallback: try the window page dropdown if sectionName isn't available
+ if activePage == "" and parent.settings.parentObject and parent.settings.parentObject.pageDD then
+ local pdd = parent.settings.parentObject.pageDD
+ if pdd.value and pdd.settings and pdd.settings.list and pdd.settings.list[pdd.value] then
+ activePage = pdd.settings.list[pdd.value]
+ end
+ end
+ local activePageSettings = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile][activePage] or {}
+ br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile][activePage] = activePageSettings
+ local data = activePageSettings
+ -- Ensure the active page is registered in the profile PageList so
+ -- option lookups (findOption) can locate values immediately.
+ br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["PageList"] = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["PageList"] or {}
+ local pageList = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["PageList"]
+ local found = false
+ for i = 1, #pageList do
+ if pageList[i] == activePage then
+ found = true
+ break
+ end
+ end
+ if not found then
+ br._G.tinsert(pageList, activePage)
+ end
local value = text .. " Check"
-- Read check value from config, false if nothing found
@@ -101,9 +121,9 @@ function br.ui:createCheckbox(parent, text, tooltip, checked)
-- Create Chat Overlay
if checked then
DiesalStyle:StyleTexture(checkBox.check, classColor)
- br.ChatOverlay("|cff15FF00" .. text .. " Enabled")
+ br.ui.chatOverlay:Show("|cff15FF00" .. text .. " Enabled")
else
- br.ChatOverlay("|cFFED0000" .. text .. " Disabled")
+ br.ui.chatOverlay:Show("|cFFED0000" .. text .. " Disabled")
end
end
)
diff --git a/System/UI/Elements/Dropdown.lua b/System/UI/Elements/Dropdown.lua
index adce08cbda..77b37dcd98 100644
--- a/System/UI/Elements/Dropdown.lua
+++ b/System/UI/Elements/Dropdown.lua
@@ -2,11 +2,32 @@ local DiesalGUI = _G.LibStub("DiesalGUI-1.0")
local DiesalTools = _G.LibStub("DiesalTools-1.0")
local _, br = ...
function br.ui:createDropdown(parent, text, itemlist, default, tooltip, tooltipDrop, hideCheckbox)
- local activePageIdx = parent.settings.parentObject.pageDD.value
- local activePage = parent.settings.parentObject.pageDD.settings.list[activePageIdx]
- br.data.settings[br.selectedSpec][br.selectedProfile][activePage] = br.data.settings[br.selectedSpec]
- [br.selectedProfile][activePage] or {}
- local data = br.data.settings[br.selectedSpec][br.selectedProfile][activePage]
+ local activePage = parent.settings.sectionName or ""
+ if activePage == "" then
+ if parent and parent.settings and parent.settings.parentObject and parent.settings.parentObject.pageDD then
+ local pdd = parent.settings.parentObject.pageDD
+ if pdd.value and pdd.settings and pdd.settings.list and pdd.settings.list[pdd.value] then
+ activePage = pdd.settings.list[pdd.value]
+ end
+ end
+ end
+ br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile][activePage] = br.data.settings[br.loader.selectedSpec]
+ [br.loader.selectedProfile][activePage] or {}
+ local data = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile][activePage]
+ -- Ensure the active page is registered in the profile PageList so
+ -- option lookups (findOption) can locate values immediately.
+ br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["PageList"] = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["PageList"] or {}
+ local pageList = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["PageList"]
+ local found = false
+ for i = 1, #pageList do
+ if pageList[i] == activePage then
+ found = true
+ break
+ end
+ end
+ if not found then
+ br._G.tinsert(pageList, activePage)
+ end
-------------------------------
----Need to calculate Y Pos----
-------------------------------
@@ -146,8 +167,8 @@ function br.ui:createProfileDropdown(parent)
end
Y = DiesalTools.Round(Y)
- local profiles = br.fetch(br.selectedSpec .. "_" .. "profiles", { { key = "default", text = "Default" } })
- -- local selectedProfile = br.fetch(br.selectedSpec .. "_" .. "profile", "default")
+ local profiles = br.ui.settingsManagement:fetch(br.loader.selectedSpec .. "_" .. "profiles", { { key = "default", text = "Default" } })
+ -- local selectedProfile = br.ui.settingsManagement:fetch(br.loader.selectedSpec .. "_" .. "profile", "default")
local profile_drop = DiesalGUI:Create("Dropdown")
parent:AddChild(profile_drop)
profile_drop:SetParent(parent.content)
@@ -159,7 +180,7 @@ function br.ui:createProfileDropdown(parent)
list[value.key] = value.key
end
profile_drop:SetList(list)
- profile_drop:SetValue(br.fetch(br.selectedSpec .. "_" .. "profile", "Default Profile"))
+ profile_drop:SetValue(br.ui.settingsManagement:fetch(br.loader.selectedSpec .. "_" .. "profile", "Default Profile"))
profile_drop:SetEventListener(
"OnValueChanged",
function(this, event, key, value, selection)
diff --git a/System/UI/Elements/PageDropdown.lua b/System/UI/Elements/PageDropdown.lua
index 5008f832f7..d379c60ef4 100644
--- a/System/UI/Elements/PageDropdown.lua
+++ b/System/UI/Elements/PageDropdown.lua
@@ -3,8 +3,22 @@ local _, br = ...
-- Dropdown for pages
-- todo: save last active page and restore
function br.ui:createPagesDropdown(window, menuPages)
- local data = br.data.settings[br.selectedSpec][br.selectedProfile]
- data["PageList"] = br.data.settings[br.selectedSpec][br.selectedProfile]["PageList"] or {}
+ if br.loader.selectedSpec == nil or br.loader.selectedProfile == nil then
+ br._G.print("Error: selectedSpec or selectedProfile is nil")
+ return
+ end
+
+ if br.data.settings[br.loader.selectedSpec] == nil then
+ br.data.settings[br.loader.selectedSpec] = {}
+ end
+
+ if br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile] == nil then
+ br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile] = {}
+ end
+
+ local data = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]
+
+ data["PageList"] = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["PageList"] or {}
if br.data.ui == nil then br.data.ui = {} end
br.data.ui["PageList"] = br.data.ui["PageList"] or {}
window.pages = menuPages
@@ -26,7 +40,7 @@ function br.ui:createPagesDropdown(window, menuPages)
local thisPage = data["PageList"][i]
if thisPage == pageName then
foundPage = true
- return
+ break
end
end
if not foundPage then
@@ -38,6 +52,34 @@ function br.ui:createPagesDropdown(window, menuPages)
newDropdown:SetList(pageNames)
+ -- newDropdown:SetEventListener(
+ -- "OnValueChanged",
+ -- function(this, event, key, value, selection)
+ -- window:ReleaseChildren()
+ -- window.currentPage = key
+ -- window.currentPageName = value
+ -- if key ~= nil then
+ -- window.pages[key][2]()
+ -- end
+ -- --Print(key.." - "..tostring(value))
+ -- br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["currentPage"] = window.currentPage
+ -- br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["totalPages"] = #window.pages
+ -- br.data.ui["currentPage"] = window.currentPage
+ -- br.data.ui["totalPages"] = #window.pages
+ -- end
+ -- )
+ -- if br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile] == nil or
+ -- br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["currentPage"] == nil
+ -- then
+ -- newDropdown:SetValue(1)
+ -- else
+ -- for i = 1, #data["PageList"] do
+ -- newDropdown:SetValue(i)
+ -- end
+ -- newDropdown:SetValue(br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["currentPage"])
+ -- end
+ -- newDropdown:ApplySettings()
+ -- window.parent:AddChild(newDropdown)
newDropdown:SetEventListener(
"OnValueChanged",
function(this, event, key, value, selection)
@@ -48,22 +90,31 @@ function br.ui:createPagesDropdown(window, menuPages)
window.pages[key][2]()
end
--Print(key.." - "..tostring(value))
- br.data.settings[br.selectedSpec][br.selectedProfile]["currentPage"] = window.currentPage
- br.data.settings[br.selectedSpec][br.selectedProfile]["totalPages"] = #window.pages
+ br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["currentPage"] = window.currentPage
+ br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["totalPages"] = #window.pages
br.data.ui["currentPage"] = window.currentPage
br.data.ui["totalPages"] = #window.pages
end
)
- if br.data.settings[br.selectedSpec][br.selectedProfile] == nil or
- br.data.settings[br.selectedSpec][br.selectedProfile]["currentPage"] == nil
+
+ -- Determine which page to show
+ local pageToShow = 1
+ if br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile] ~= nil and
+ br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["currentPage"] ~= nil
then
- newDropdown:SetValue(1)
- else
- for i = 1, #data["PageList"] do
- newDropdown:SetValue(i)
- end
- newDropdown:SetValue(br.data.settings[br.selectedSpec][br.selectedProfile]["currentPage"])
+ pageToShow = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["currentPage"]
end
+
+ -- Set the dropdown value
+ newDropdown:SetValue(pageToShow)
newDropdown:ApplySettings()
window.parent:AddChild(newDropdown)
+
+ -- Manually trigger the page load since SetValue doesn't fire the event
+ window:ReleaseChildren()
+ window.currentPage = pageToShow
+ window.currentPageName = pageNames[pageToShow]
+ if window.pages[pageToShow] and window.pages[pageToShow][2] then
+ window.pages[pageToShow][2]()
+ end
end
diff --git a/System/UI/Elements/RotationDropdown.lua b/System/UI/Elements/RotationDropdown.lua
index 8819feccc1..57d2c34844 100644
--- a/System/UI/Elements/RotationDropdown.lua
+++ b/System/UI/Elements/RotationDropdown.lua
@@ -12,25 +12,25 @@ function br.ui:createRotationDropdown(parent, itemlist, tooltip)
-- Set selected profile to 1 if not found
-- br:loadLastProfileTracker()
- if br.data.settings[br.selectedSpec][text .. "Drop"] == nil then
- br.data.settings[br.selectedSpec][text .. "Drop"] = 1
- elseif br.data.settings[br.selectedSpec][text .. "Drop"] > #itemlist then
+ if br.data.settings[br.loader.selectedSpec][text .. "Drop"] == nil then
+ br.data.settings[br.loader.selectedSpec][text .. "Drop"] = 1
+ elseif br.data.settings[br.loader.selectedSpec][text .. "Drop"] > #itemlist then
--[[ Rest the profile which is no longer found
If someone adds a profile then the old options from profile befopre would be loaded
--]]
- local notFoundProfile = br.data.settings[br.selectedSpec][text .. "Drop"]
- br.data.settings[br.selectedSpec][notFoundProfile] = {}
+ local notFoundProfile = br.data.settings[br.loader.selectedSpec][text .. "Drop"]
+ br.data.settings[br.loader.selectedSpec][notFoundProfile] = {}
- br.data.settings[br.selectedSpec][text .. "Drop"] = 1
+ br.data.settings[br.loader.selectedSpec][text .. "Drop"] = 1
br._G.print("Selected profile not found fallback to profile 1.")
end
- -- if br.data.tracker ~= nil and br.data.tracker.lastProfile ~= nil then br.data.settings[br.selectedSpec][text.."Drop"] = br.data.tracker.lastProfile end
+ -- if br.data.tracker ~= nil and br.data.tracker.lastProfile ~= nil then br.data.settings[br.loader.selectedSpec][text.."Drop"] = br.data.tracker.lastProfile end
-- Set Values
- local value = br.data.settings[br.selectedSpec][text .. "Drop"]
- br.selectedProfile = value
- br.selectedProfileName = itemlist[value]
- br.settingsFile = itemlist[value] .. ".lua"
+ local value = br.data.settings[br.loader.selectedSpec][text .. "Drop"]
+ br.loader.selectedProfile = value
+ br.loader.selectedProfileName = itemlist[value]
+ br.loader.settingsFile = itemlist[value] .. ".lua"
newDropdown:SetValue(value)
-- br.ui:loadWindowPositions("config")
-- br.ui:loadWindowPositions("profile")
@@ -41,17 +41,18 @@ function br.ui:createRotationDropdown(parent, itemlist, tooltip)
if br._G.UnitAffectingCombat("player") then
br._G.print("Unable to change profile in combat. Please try again when combat ends.")
else
- -- br.ui:recreateWindows()
- -- br._G.print("Rotation Changed")
- br:saveSettings(nil, nil, br.selectedSpec, br.selectedProfileName)
- br:saveLastProfileTracker()
- br.data.settings[br.selectedSpec][text .. "Drop"] = key
- br.data.tracker[br.selectedSpec][text .. "Drop"] = key
- br.data.settings[br.selectedSpec][text .. "DropValue"] = value
- br.data.tracker[br.selectedSpec][text .. "DropValue"] = value
+ -- Update selection first so saves go to the new profile
+ br.data.settings[br.loader.selectedSpec][text .. "Drop"] = key
+ br.data.tracker[br.loader.selectedSpec][text .. "Drop"] = key
+ br.data.settings[br.loader.selectedSpec][text .. "DropValue"] = value
+ br.data.tracker[br.loader.selectedSpec][text .. "DropValue"] = value
br.data.lastProfile = key
- br.selectedProfile = key
- br.selectedProfileName = value
+ br.loader.selectedProfile = key
+ br.loader.selectedProfileName = value
+
+ -- Now persist the new profile selection and update tracker
+ br.ui.settingsManagement:saveSettings(nil, nil, br.loader.selectedSpec, br.loader.selectedProfileName)
+ br.ui.settingsManagement:saveLastProfileTracker()
end
br.rotationChanged = true
end
diff --git a/System/UI/Elements/ScrollingEditBox.lua b/System/UI/Elements/ScrollingEditBox.lua
index 072f376a60..266baf4656 100644
--- a/System/UI/Elements/ScrollingEditBox.lua
+++ b/System/UI/Elements/ScrollingEditBox.lua
@@ -6,9 +6,24 @@ local _, br = ...
function br.ui:createScrollingEditBox(parent, text, content, tooltip, width, height, hideCheckbox)
local activePageIdx = parent.settings.parentObject.pageDD.value
local activePage = parent.settings.parentObject.pageDD.settings.list[activePageIdx]
- br.data.settings[br.selectedSpec][br.selectedProfile][activePage] = br.data.settings[br.selectedSpec]
- [br.selectedProfile][activePage] or {}
- local data = br.data.settings[br.selectedSpec][br.selectedProfile][activePage]
+ br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile][activePage] = br.data.settings[br.loader.selectedSpec]
+ [br.loader.selectedProfile][activePage] or {}
+ local data = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile][activePage]
+
+ -- Ensure the active page is registered in the profile PageList so
+ -- option lookups (findOption) can locate values immediately.
+ br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["PageList"] = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["PageList"] or {}
+ local pageList = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["PageList"]
+ local found = false
+ for i = 1, #pageList do
+ if pageList[i] == activePage then
+ found = true
+ break
+ end
+ end
+ if not found then
+ br._G.tinsert(pageList, activePage)
+ end
if width == nil then
width = 240
diff --git a/System/UI/Elements/Section.lua b/System/UI/Elements/Section.lua
index 8296b6ab8a..91152775f9 100644
--- a/System/UI/Elements/Section.lua
+++ b/System/UI/Elements/Section.lua
@@ -13,18 +13,18 @@ function br.ui:createSection(parent, sectionName, tooltip)
newSection.settings.position = #parent.children + 1
end
newSection.settings.sectionName = sectionName
- if br.data.settings[br.selectedSpec][br.selectedProfile] == nil then
- br.data.settings[br.selectedSpec][br.selectedProfile] = {}
+ if br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile] == nil then
+ br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile] = {}
end
newSection.settings.expanded =
- br.data.settings[br.selectedSpec][br.selectedProfile][sectionName .. " Section"] or true
+ br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile][sectionName .. " Section"] or true
br.data.ui[sectionName .. " Section"] = newSection.settings.expanded
-- newSection.settings.contentPadding = {0,0,12,32}
newSection:SetEventListener(
"OnStateChange",
function(this, event)
- br.data.settings[br.selectedSpec][br.selectedProfile][sectionName .. " Section"] =
+ br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile][sectionName .. " Section"] =
newSection.settings.expanded
br.data.ui[sectionName .. " Section"] = newSection.settings.expanded
end
@@ -76,7 +76,7 @@ end
-- Restore last saved state of section (collapsed or expanded)
function br.ui:checkSectionState(section)
- local state = br.data.settings[br.selectedSpec][br.selectedProfile][section.settings.sectionName .. " Section"]
+ local state = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile][section.settings.sectionName .. " Section"]
if state then
diff --git a/System/UI/Elements/Spinner.lua b/System/UI/Elements/Spinner.lua
index a561e1704c..caa3a9146d 100644
--- a/System/UI/Elements/Spinner.lua
+++ b/System/UI/Elements/Spinner.lua
@@ -2,11 +2,32 @@ local DiesalGUI = _G.LibStub("DiesalGUI-1.0")
local DiesalTools = _G.LibStub("DiesalTools-1.0")
local _, br = ...
function br.ui:createSpinner(parent, text, number, min, max, step, tooltip, tooltipSpin, hideCheckbox)
- local activePageIdx = parent.settings.parentObject.pageDD.value
- local activePage = parent.settings.parentObject.pageDD.settings.list[activePageIdx]
- br.data.settings[br.selectedSpec][br.selectedProfile][activePage] = br.data.settings[br.selectedSpec]
- [br.selectedProfile][activePage] or {}
- local data = br.data.settings[br.selectedSpec][br.selectedProfile][activePage]
+ local activePage = parent.settings.sectionName or ""
+ if activePage == "" then
+ if parent and parent.settings and parent.settings.parentObject and parent.settings.parentObject.pageDD then
+ local pdd = parent.settings.parentObject.pageDD
+ if pdd.value and pdd.settings and pdd.settings.list and pdd.settings.list[pdd.value] then
+ activePage = pdd.settings.list[pdd.value]
+ end
+ end
+ end
+ br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile][activePage] = br.data.settings[br.loader.selectedSpec]
+ [br.loader.selectedProfile][activePage] or {}
+ local data = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile][activePage]
+ -- Ensure the active page is registered in the profile PageList so
+ -- option lookups (findOption) can locate values immediately.
+ br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["PageList"] = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["PageList"] or {}
+ local pageList = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["PageList"]
+ local found = false
+ for i = 1, #pageList do
+ if pageList[i] == activePage then
+ found = true
+ break
+ end
+ end
+ if not found then
+ br._G.tinsert(pageList, activePage)
+ end
-------------------------------
----Need to calculate Y Pos----
-------------------------------
@@ -123,13 +144,20 @@ end
-- Spinner Object : {number, min, max, step, tooltip}
function br.ui:createDoubleSpinner(parent, text, spinner1, spinner2, hideCheckbox)
- local activePageIdx = parent.settings.parentObject.pageDD.value
- local activePage = parent.settings.parentObject.pageDD.settings.list[activePageIdx]
+ local activePageIdx = 1
+ local activePage = ""
+ if parent and parent.settings and parent.settings.parentObject and parent.settings.parentObject.pageDD then
+ local pdd = parent.settings.parentObject.pageDD
+ if pdd.value and pdd.settings and pdd.settings.list and pdd.settings.list[pdd.value] then
+ activePageIdx = pdd.value
+ activePage = pdd.settings.list[activePageIdx]
+ end
+ end
-- string.gsub(parent.settings.parentObject.pageDD.settings.list[activePageIdx], " Options", "")
-- activePage = string.gsub(activePage, " ", "")
- br.data.settings[br.selectedSpec][br.selectedProfile][activePage] = br.data.settings[br.selectedSpec]
- [br.selectedProfile][activePage] or {}
- local data = br.data.settings[br.selectedSpec][br.selectedProfile][activePage]
+ br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile][activePage] = br.data.settings[br.loader.selectedSpec]
+ [br.loader.selectedProfile][activePage] or {}
+ local data = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile][activePage]
-------------------------------
----Need to calculate Y Pos----
-------------------------------
diff --git a/System/UI/Elements/Text.lua b/System/UI/Elements/Text.lua
index 747223bdd9..dd6223e8a0 100644
--- a/System/UI/Elements/Text.lua
+++ b/System/UI/Elements/Text.lua
@@ -34,6 +34,11 @@ function br.ui:createText(parent, text, isCheckbox)
label = label.fontString
+ -- Ensure font is set before using the FontString
+ if not label:GetFont() then
+ label:SetFontObject(_G.GameFontNormal)
+ end
+
label:SetPoint("TOPLEFT", parent.content, "TOPLEFT", 20, Y)
label:SetWidth(parent.content:GetWidth() - 10)
label:SetJustifyH("LEFT")
diff --git a/System/UI/Elements/Window.lua b/System/UI/Elements/Window.lua
index 2480b86d6d..94906e51f6 100644
--- a/System/UI/Elements/Window.lua
+++ b/System/UI/Elements/Window.lua
@@ -8,7 +8,7 @@ function br.ui:createWindow(name, width, height, title, color, messageWindow)
title = name
end
if color == nil then
- color = br.classColor
+ color = br.ui.colors.class
end
local window = DiesalGUI:Create("Window")
window:SetTitle(color .. "BadRotations", title)
@@ -23,7 +23,7 @@ function br.ui:createWindow(name, width, height, title, color, messageWindow)
"OnClick",
function(this, button)
br.ui:savePosition(name)
- br.data.settings[br.selectedSpec][name]["active"] = false
+ br.data.settings[br.loader.selectedSpec][name]["active"] = false
DiesalGUI:OnMouse(this, button)
br._G.PlaySound(799)
window:FireEvent("OnClose")
@@ -42,14 +42,14 @@ function br.ui:createWindow(name, width, height, title, color, messageWindow)
scrollFrame:SetAllPoints(window.content)
scrollFrame.parent = window
- if br.data.settings and br.data.settings[br.selectedSpec] == nil then
- br.data.settings[br.selectedSpec] = {}
+ if br.data.settings and br.data.settings[br.loader.selectedSpec] == nil then
+ br.data.settings[br.loader.selectedSpec] = {}
end
- if br.data.settings and br.data.settings[br.selectedSpec] and br.data.settings[br.selectedSpec][name] == nil then
- br.data.settings[br.selectedSpec][name] = {}
+ if br.data.settings and br.data.settings[br.loader.selectedSpec] and br.data.settings[br.loader.selectedSpec][name] == nil then
+ br.data.settings[br.loader.selectedSpec][name] = {}
end
- if br.data.settings[br.selectedSpec][name] then
- local windows = br.data.settings[br.selectedSpec][name]
+ if br.data.settings[br.loader.selectedSpec][name] then
+ local windows = br.data.settings[br.loader.selectedSpec][name]
if windows["point"] ~= nil then
local point, relativeTo = windows["point"], windows["relativeTo"]
local relativePoint = windows["relativePoint"]
@@ -82,13 +82,13 @@ function br.ui:loadWindowPositions(thisWindow)
if scrollFrame == nil then
return
end
- if br.data.settings[br.selectedSpec] == nil then
- br.data.settings[br.selectedSpec] = {}
+ if br.data.settings[br.loader.selectedSpec] == nil then
+ br.data.settings[br.loader.selectedSpec] = {}
end
- if br.data.settings[br.selectedSpec][thisWindow] == nil then
- br.data.settings[br.selectedSpec][thisWindow] = {}
+ if br.data.settings[br.loader.selectedSpec][thisWindow] == nil then
+ br.data.settings[br.loader.selectedSpec][thisWindow] = {}
end
- local windows = br.data.settings[br.selectedSpec][thisWindow]
+ local windows = br.data.settings[br.loader.selectedSpec][thisWindow]
if windows["point"] ~= nil then
local point, relativeTo = windows["point"], windows["relativeTo"]
local relativePoint = windows["relativePoint"]
@@ -108,10 +108,10 @@ function br.ui:loadWindowPositions(thisWindow)
end
function br.ui:checkWindowStatus(windowName)
- if br.data.settings[br.selectedSpec][windowName] == nil then
- br.data.settings[br.selectedSpec][windowName] = {}
+ if br.data.settings[br.loader.selectedSpec][windowName] == nil then
+ br.data.settings[br.loader.selectedSpec][windowName] = {}
end
- local windows = br.data.settings[br.selectedSpec][windowName]
+ local windows = br.data.settings[br.loader.selectedSpec][windowName]
if windows["active"] == true or windows["active"] == nil then
if br.ui.window[windowName].parent then
br.ui.window[windowName].parent:Show()
@@ -126,30 +126,30 @@ function br.ui:checkWindowStatus(windowName)
end
function br.ui:savePosition(windowName)
- if br.data.settings[br.selectedSpec] == nil then
- br.data.settings[br.selectedSpec] = {}
+ if br.data.settings[br.loader.selectedSpec] == nil then
+ br.data.settings[br.loader.selectedSpec] = {}
end
- if br.data.settings[br.selectedSpec][windowName] == nil then
- br.data.settings[br.selectedSpec][windowName] = {}
+ if br.data.settings[br.loader.selectedSpec][windowName] == nil then
+ br.data.settings[br.loader.selectedSpec][windowName] = {}
end
if br.ui.window[windowName] ~= nil then
if br.ui.window[windowName].parent ~= nil then
local point, relativeTo, relativePoint, xOfs, yOfs = br.ui.window[windowName].parent:GetPoint(1)
- br.data.settings[br.selectedSpec][windowName]["point"] = point
- br.data.settings[br.selectedSpec][windowName]["relativeTo"] = relativeTo:GetName()
- br.data.settings[br.selectedSpec][windowName]["relativePoint"] = relativePoint
- br.data.settings[br.selectedSpec][windowName]["xOfs"] = xOfs
- br.data.settings[br.selectedSpec][windowName]["yOfs"] = yOfs
+ br.data.settings[br.loader.selectedSpec][windowName]["point"] = point
+ br.data.settings[br.loader.selectedSpec][windowName]["relativeTo"] = relativeTo:GetName()
+ br.data.settings[br.loader.selectedSpec][windowName]["relativePoint"] = relativePoint
+ br.data.settings[br.loader.selectedSpec][windowName]["xOfs"] = xOfs
+ br.data.settings[br.loader.selectedSpec][windowName]["yOfs"] = yOfs
point, relativeTo, relativePoint, xOfs, yOfs = br.ui.window[windowName].parent:GetPoint(2)
if point then
- br.data.settings[br.selectedSpec][windowName]["point2"] = point
- br.data.settings[br.selectedSpec][windowName]["relativeTo2"] = relativeTo:GetName()
- br.data.settings[br.selectedSpec][windowName]["relativePoint2"] = relativePoint
- br.data.settings[br.selectedSpec][windowName]["xOfs2"] = xOfs
- br.data.settings[br.selectedSpec][windowName]["yOfs2"] = yOfs
+ br.data.settings[br.loader.selectedSpec][windowName]["point2"] = point
+ br.data.settings[br.loader.selectedSpec][windowName]["relativeTo2"] = relativeTo:GetName()
+ br.data.settings[br.loader.selectedSpec][windowName]["relativePoint2"] = relativePoint
+ br.data.settings[br.loader.selectedSpec][windowName]["xOfs2"] = xOfs
+ br.data.settings[br.loader.selectedSpec][windowName]["yOfs2"] = yOfs
end
- br.data.settings[br.selectedSpec][windowName]["width"] = br.ui.window[windowName].parent:GetWidth()
- br.data.settings[br.selectedSpec][windowName]["height"] = br.ui.window[windowName].parent:GetHeight()
+ br.data.settings[br.loader.selectedSpec][windowName]["width"] = br.ui.window[windowName].parent:GetWidth()
+ br.data.settings[br.loader.selectedSpec][windowName]["height"] = br.ui.window[windowName].parent:GetHeight()
end
end
end
@@ -167,7 +167,7 @@ function br.ui:showWindow(windowName)
if k == windowName then
if br.ui.window[k].parent ~= nil then
br.ui.window[k].parent:Show()
- br.data.settings[br.selectedSpec][k].active = true
+ br.data.settings[br.loader.selectedSpec][k].active = true
end
end
end
@@ -189,8 +189,8 @@ function br.ui:closeWindow(windowName)
for m, _ in pairs(br.data.settings[tostring(l)]) do
if m == k then
if
- br.data.settings[br.selectedSpec].toggles ~= nil and
- br.data.settings[br.selectedSpec].toggles["Power"] ~= 1
+ br.data.settings[br.loader.selectedSpec].toggles ~= nil and
+ br.data.settings[br.loader.selectedSpec].toggles["Power"] ~= 1
then
if br.data.settings[l][m].active == nil or br.data.settings[l][m].active then
br.ui.window[k].parent.closeButton:Click()
@@ -214,16 +214,16 @@ function br.ui:toggleWindow(windowName)
for k, _ in pairs(br.ui.window) do
if k == windowName then
if br.ui.window[k].parent ~= nil then
- if br.data.settings[br.selectedSpec][k] ~= nil then
- if br.data.settings[br.selectedSpec][k].active == nil then
+ if br.data.settings[br.loader.selectedSpec][k] ~= nil then
+ if br.data.settings[br.loader.selectedSpec][k].active == nil then
br.ui.window[k].parent:Show()
- br.data.settings[br.selectedSpec][k].active = true
- elseif br.data.settings[br.selectedSpec][k].active then
+ br.data.settings[br.loader.selectedSpec][k].active = true
+ elseif br.data.settings[br.loader.selectedSpec][k].active then
br.ui.window[k].parent.closeButton:Click()
- br.data.settings[br.selectedSpec][k].active = false
+ br.data.settings[br.loader.selectedSpec][k].active = false
else
br.ui.window[k].parent:Show()
- br.data.settings[br.selectedSpec][k].active = true
+ br.data.settings[br.loader.selectedSpec][k].active = true
end
end
end
@@ -232,7 +232,10 @@ function br.ui:toggleWindow(windowName)
end
function br.ui:recreateWindows()
- br.ui:closeWindow("all")
+ -- Close only config and debug windows, not profile window
+ -- Profile window is created by br.player:createOptions() and should not be destroyed here
+ br.ui:closeWindow("config")
+ br.ui:closeWindow("debug")
br.ui:createConfigWindow()
br.ui:createDebugWindow()
end
diff --git a/System/UI/MinimapButton.lua b/System/UI/MinimapButton.lua
index d31ed3137e..6f486a60b8 100644
--- a/System/UI/MinimapButton.lua
+++ b/System/UI/MinimapButton.lua
@@ -1,6 +1,9 @@
local _, br = ...
+br.ui.minimapButton = br.ui.minimapButton or {}
+local minimapButton = br.ui.minimapButton
+
-- Minimap Button
-function br:MinimapButton()
+function br.ui:MinimapButton()
-- Function to define the shapes of the minimap
local function defineMinimapShapes()
return {
@@ -71,12 +74,12 @@ function br:MinimapButton()
update_position(self, pos)
end
- -- Function to create the BadRotationsButton and set its properties
- local function createBadRotationsButton()
- br.BadRotationsButton = br._G.CreateFrame("Button", "BadRotationsButton", br._G.Minimap)
- br.BadRotationsButton:SetHeight(25)
- br.BadRotationsButton:SetWidth(25)
- br.BadRotationsButton:SetFrameStrata("MEDIUM")
+ -- Function to create the MinimapButton and set its properties
+ local function createMinimapButton()
+ br.ui.minimapButton.frame = br._G.CreateFrame("Button", "BadRotationsButton", br._G.Minimap)
+ br.ui.minimapButton.frame:SetHeight(25)
+ br.ui.minimapButton.frame:SetWidth(25)
+ br.ui.minimapButton.frame:SetFrameStrata("MEDIUM")
local x, y
if not br.data.settings.minimapButton then
x = 75.70
@@ -85,12 +88,18 @@ function br:MinimapButton()
x = br.data.settings.minimapButton.pos.x or 75.70
y = br.data.settings.minimapButton.pos.y or -6.63
end
- br.BadRotationsButton:SetPoint("CENTER", x, y)
- br.BadRotationsButton:SetMovable(true)
- br.BadRotationsButton:SetUserPlaced(true)
- br.BadRotationsButton:SetNormalTexture("Interface\\HelpFrame\\HotIssueIcon.blp")
- br.BadRotationsButton:SetPushedTexture("Interface\\HelpFrame\\HotIssueIcon.blp")
- br.BadRotationsButton:SetHighlightTexture("Interface\\Minimap\\UI-Minimap-Background.blp")
+ -- Ensure explicit anchoring to the Minimap and validate x/y
+ br.ui.minimapButton.frame:ClearAllPoints()
+ if type(x) == "number" and type(y) == "number" and br._G.Minimap then
+ br.ui.minimapButton.frame:SetPoint("CENTER", br._G.Minimap, "CENTER", x, y)
+ else
+ br.ui.minimapButton.frame:SetPoint("CENTER", br._G.Minimap or br._G.UIParent, "CENTER", 75.70, -6.63)
+ end
+ br.ui.minimapButton.frame:SetMovable(true)
+ br.ui.minimapButton.frame:SetUserPlaced(true)
+ br.ui.minimapButton.frame:SetNormalTexture("Interface\\HelpFrame\\HotIssueIcon.blp")
+ br.ui.minimapButton.frame:SetPushedTexture("Interface\\HelpFrame\\HotIssueIcon.blp")
+ br.ui.minimapButton.frame:SetHighlightTexture("Interface\\Minimap\\UI-Minimap-Background.blp")
end
local function handleOnMouseDown(self, button)
@@ -119,15 +128,15 @@ function br:MinimapButton()
end
if button == "LeftButton" then
if br._G.IsShiftKeyDown() and not br._G.IsAltKeyDown() and not br._G.UnitAffectingCombat("player") then
- if br.data.settings[br.selectedSpec].toggles["Main"] == 1 then
- br.data.settings[br.selectedSpec].toggles["Main"] = 0
- br.mainButton:Hide()
+ if br.data.settings[br.loader.selectedSpec].toggles["Main"] == 1 then
+ br.data.settings[br.loader.selectedSpec].toggles["Main"] = 0
+ br.ui.toggles.mainButton:Hide()
else
- br.data.settings[br.selectedSpec].toggles["Main"] = 1
- br.mainButton:Show()
+ br.data.settings[br.loader.selectedSpec].toggles["Main"] = 1
+ br.ui.toggles.mainButton:Show()
end
elseif br._G.IsShiftKeyDown() and not br._G.IsAltKeyDown() and br._G.UnitAffectingCombat("player") then
- br._G_.print("Combat Lockdown detected. Unable to modify br.BadRotationsButton bar. Please try again when out of combat.")
+ br._G_.print("Combat Lockdown detected. Unable to modify minimap button. Please try again when out of combat.")
elseif not br._G.IsShiftKeyDown() and not br._G.IsAltKeyDown() then
br.ui:toggleWindow("config")
end
@@ -150,10 +159,10 @@ function br:MinimapButton()
br._G.GameTooltip:Hide()
end
- createBadRotationsButton()
- br.BadRotationsButton:SetScript("OnMouseDown", handleOnMouseDown)
- br.BadRotationsButton:SetScript("OnMouseUp", handleOnMouseUp)
- br.BadRotationsButton:SetScript("OnClick", handleOnClick)
- br.BadRotationsButton:SetScript("OnEnter", handleOnEnter)
- br.BadRotationsButton:SetScript("OnLeave", handleOnLeave)
+ createMinimapButton()
+ br.ui.minimapButton.frame:SetScript("OnMouseDown", handleOnMouseDown)
+ br.ui.minimapButton.frame:SetScript("OnMouseUp", handleOnMouseUp)
+ br.ui.minimapButton.frame:SetScript("OnClick", handleOnClick)
+ br.ui.minimapButton.frame:SetScript("OnEnter", handleOnEnter)
+ br.ui.minimapButton.frame:SetScript("OnLeave", handleOnLeave)
end
diff --git a/System/UI/SettingsManagement.lua b/System/UI/SettingsManagement.lua
index 0ff5e65864..2846c44649 100644
--- a/System/UI/SettingsManagement.lua
+++ b/System/UI/SettingsManagement.lua
@@ -1,5 +1,6 @@
local _, br = ...
-
+br.ui.settingsManagement = br.ui.settingsManagement or {}
+local settingsManagement = br.ui.settingsManagement
local sep = IsMacClient() and "/" or "\\"
local function getFolderClassName(class)
@@ -25,15 +26,291 @@ local function checkDirectory(dir)
end
end
+local function cleanOrphanedSettings(specID)
+ if not br.data or not br.data.settings or not br.data.settings[specID] then
+ return
+ end
+
+ local currentSettings = br.data.settings[specID]
+
+ -- Only clean options - toggles and modes are small and mostly static
+ if not currentSettings.options or not br.ui or not br.ui.options or not br.ui.options.options then
+ return
+ end
+
+ -- Build quick lookup of valid option keys
+ local validOptions = {}
+ for _, optionDef in pairs(br.ui.options.options) do
+ if type(optionDef) == "table" and optionDef.key then
+ validOptions[optionDef.key] = true
+ end
+ end
+
+ -- Remove orphaned options
+ for key in pairs(currentSettings.options) do
+ if not validOptions[key] then
+ currentSettings.options[key] = nil
+ end
+ end
+end
+
+--------------------------------------------------------------------------
+-- Table Save/Load code from: http://lua-users.org/wiki/SaveTableToFile --
+--------------------------------------------------------------------------
+local function exportstring(s)
+ return string.format("%q", s)
+end
+
+-- The Save Function
+local tableSave = function(tbl, filename)
+ local charS, charE = " ", "\n"
+ -- local file,err = io.open( filename, "wb" )
+ -- if err then return err end
+ -- br._G.WriteFile(filename, "", false)
+ local content = ""
+ -- initiate variables for save procedure
+ local tables, lookup = { tbl }, { [tbl] = 1 }
+ -- file:write( "return {"..charE )
+ -- br._G.WriteFile(filename, "return {" .. charE, true)
+ content = content .. "return {" .. charE
+
+ for idx, t in ipairs(tables) do
+ -- file:write( "-- Table: {"..idx.."}"..charE )
+ -- br._G.WriteFile(filename, "-- Table: {" .. idx .. "}" .. charE, true)
+ content = content .. "-- Table: {" .. idx .. "}" .. charE
+ -- file:write( "{"..charE )
+ -- br._G.WriteFile(filename, "{" .. charE, true)
+ content = content .. "{" .. charE
+ local thandled = {}
+
+ for i, v in ipairs(t) do
+ thandled[i] = true
+ local stype = type(v)
+ -- only handle value
+ if stype == "table" then
+ if not lookup[v] then
+ table.insert(tables, v)
+ lookup[v] = #tables
+ end
+ -- file:write( charS.."{"..lookup[v].."},"..charE )
+ -- br._G.WriteFile(filename, charS .. "{" .. lookup[v] .. "}," .. charE, true)
+ content = content .. charS .. "{" .. lookup[v] .. "}," .. charE
+ elseif stype == "string" then
+ -- file:write( charS..exportstring( v )..","..charE )
+ -- br._G.WriteFile(filename, charS .. exportstring(v) .. "," .. charE, true)
+ content = content .. charS .. exportstring(v) .. "," .. charE
+ elseif stype == "number" or stype == "boolean" then
+ -- file:write( charS..tostring( v )..","..charE )
+ -- br._G.WriteFile(filename, charS .. tostring(v) .. "," .. charE, true)
+ content = content .. charS .. tostring(v) .. "," .. charE
+ end
+ end
+
+ for i, v in pairs(t) do
+ -- escape handled values
+ if (not thandled[i]) then
+ local str = ""
+ local stype = type(i)
+ -- handle index
+ if stype == "table" then
+ if not lookup[i] then
+ table.insert(tables, i)
+ lookup[i] = #tables
+ end
+ str = charS .. "[{" .. lookup[i] .. "}]="
+ elseif stype == "string" then
+ str = charS .. "[" .. exportstring(i) .. "]="
+ elseif stype == "number" then
+ str = charS .. "[" .. tostring(i) .. "]="
+ elseif stype == "boolean" then
+ str = charS .. "[" .. i .. "]="
+ end
+
+ if str ~= "" then
+ stype = type(v)
+ -- handle value
+ if stype == "table" then
+ if not lookup[v] then
+ table.insert(tables, v)
+ lookup[v] = #tables
+ end
+ -- file:write( str.."{"..lookup[v].."},"..charE )
+ -- br._G.WriteFile(filename, str .. "{" .. lookup[v] .. "}," .. charE, true)
+ content = content .. str .. "{" .. lookup[v] .. "}," .. charE
+ elseif stype == "string" then
+ -- file:write( str..exportstring( v )..","..charE )
+ -- br._G.WriteFile(filename, str .. exportstring(v) .. "," .. charE, true)
+ content = content .. str .. exportstring(v) .. "," .. charE
+ elseif stype == "number" or stype == "boolean" then
+ -- file:write( str..tostring( v )..","..charE )
+ -- br._G.WriteFile(filename, str .. tostring(v) .. "," .. charE, true)
+ content = content .. str .. tostring(v) .. "," .. charE
+ end
+ end
+ end
+ end
+ -- file:write( "},"..charE )
+ -- br._G.WriteFile(filename, "}," .. charE, true)
+ content = content .. "}," .. charE
+ end
+ -- file:write( "}" )
+ -- br._G.WriteFile(filename, "}", true)
+ content = content .. "}"
+ -- file:close()
+ br._G.WriteFile(filename, content, false)
+end
+
+-- The Load Function
+local tableLoad = function(sfile)
+ local file = br._G.ReadFile(sfile)
+ if file == nil or file == "" then
+ return
+ end
+ local ftables = br._G.loadstring(file, sfile)
+ -- local ftables,err = loadfile( sfile )
+ -- if err then return _,err end
+ local tables
+ if (ftables) then
+ tables = ftables()
+ else
+ tables = {}
+ end
+ -- return deepcopy(tables)
+ for idx = 1, #tables do
+ local tolinki = {}
+ for i, v in pairs(tables[idx]) do
+ if type(v) == "table" then
+ tables[idx][i] = tables[v[1]]
+ end
+ if type(i) == "table" and tables[i[1]] then
+ table.insert(tolinki, { i, tables[i[1]] })
+ end
+ end
+ -- link indices
+ for _, v in ipairs(tolinki) do
+ tables[idx][v[2]], tables[idx][v[1]] = tables[idx][v[1]], nil
+ end
+ end
+ return tables[1]
+end
+
+-- Default Settings
+function settingsManagement:defaultSettings()
+ br._G.C_Timer.After(2, function()
+ if br.ui.window.config == nil then
+ br.ui.window.config = {}
+ end
+ if br.ui.window.config.frame == nil then
+ br.ui:createConfigWindow()
+ end
+ br.ui:toggleWindow("config")
+ br.ui:closeWindow("config")
+ end)
+ -- Settings Per Spec
+ if br.data.settings[br.loader.selectedSpec].toggles == nil then
+ br.data.settings[br.loader.selectedSpec].toggles = {}
+ end
+ if br.data.settings[br.loader.selectedSpec]["RotationDrop"] == nil then
+ br.loader.selectedProfile = 1
+ else
+ br.loader.selectedProfile = br.data.settings[br.loader.selectedSpec]["RotationDrop"]
+ end
+ if br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile] == nil then
+ br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile] = {}
+ end
+ -- Define Main Button if no settings exist
+ if (br.data.settings and br.data.settings.mainButton == nil) then
+ br.data.settings.mainButton = {
+ pos = {
+ anchor = "CENTER",
+ x = -75,
+ y = -200
+ }
+ }
+ br.data.settings.buttonSize = 32
+ br.data.settings.font = "Fonts/arialn.ttf"
+ br.data.settings.fontsize = 16
+ br.data.settings.wiped = true
+ end
+ -- Define Minimap Button if no settings exist
+ if (br.data.settings and br.data.settings.minimapButton == nil) then
+ br.data.settings.minimapButton = {
+ pos = {
+ x = 75.70,
+ y = -6.63
+ }
+ }
+ end
+end
+
+-- Load Saved Settings
+settingsManagement.initializeSettings = false
+function settingsManagement:loadSavedSettings()
+ -- Run initialization either when explicitly requested via initializeSettings
+ -- or when settings have not yet been loaded (e.g. on login) or a rotation changed
+ if settingsManagement.initializeSettings or (not (br.data and br.data.loadedSettings) or br.rotationChanged) then
+ br.loader.cBuilder:loadProfiles()
+ local specID = br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization())
+ if not (br.loader.noRotationsFound and br.loader.noRotationsFound[specID]) then
+ settingsManagement:loadLastProfileTracker()
+ end
+ if br.data.settings[br.loader.selectedSpec]["RotationDropValue"] then
+ settingsManagement:loadSettings(nil, nil, nil, br.data.settings[br.loader.selectedSpec]["RotationDropValue"])
+ elseif br.loader.rotations[br.loader.selectedSpec] then
+ settingsManagement:loadSettings(nil, nil, nil, br.loader.rotations[br.loader.selectedSpec][1].name)
+ else
+ if not br.loader.rotations[br.loader.selectedSpec] then
+ settingsManagement.initializeSettings = false
+ return
+ end
+ end
+ settingsManagement:defaultSettings()
+ -- Build the Toggles
+ br.ui.toggles:TogglesFrame()
+ -- Restore Minimap Button Position (validate values and anchor to Minimap)
+ do
+ local mx = br.data.settings.minimapButton and br.data.settings.minimapButton.pos and br.data.settings.minimapButton.pos.x
+ local my = br.data.settings.minimapButton and br.data.settings.minimapButton.pos and br.data.settings.minimapButton.pos.y
+ if br.ui.minimapButton and br.ui.minimapButton.frame then
+ br.ui.minimapButton.frame:ClearAllPoints()
+ if type(mx) == "number" and type(my) == "number" and br._G.Minimap then
+ br.ui.minimapButton.frame:SetPoint("CENTER", br._G.Minimap, "CENTER", mx, my)
+ else
+ -- Fallback to sensible defaults if saved data is invalid
+ br.ui.minimapButton.frame:SetPoint("CENTER", br._G.Minimap or br._G.UIParent, "CENTER", 75.70, -6.63)
+ end
+ end
+ end
+ settingsManagement.initializeSettings = false
+ end
+end
+
+settingsManagement.profile = settingsManagement.profile or {}
+function settingsManagement:store(key, value)
+ if settingsManagement.profile == nil then
+ settingsManagement.profile = {}
+ end
+ settingsManagement.profile[key] = value
+ return true
+end
+
+function settingsManagement:fetch(key, default)
+ if settingsManagement.profile == nil then
+ settingsManagement.profile = {}
+ end
+ local value = settingsManagement.profile[key]
+ return value == nil and default or value
+end
+
-- Check Directories
-function br:checkDirectories(folder, class, spec, profile, instance)
+function settingsManagement:checkDirectories(folder, class, spec, profile, instance)
-- Set Settings Directory
local wowDir = br._G.GetWoWDirectory() or ""
local mainDir = ""
if wowDir:match('_retail_') then
- mainDir = wowDir .. sep .. "Interface" .. sep .. "AddOns" .. sep .. br.addonName .. sep .. "Settings" .. sep
+ mainDir = wowDir .. sep .. "Interface" .. sep .. "AddOns" .. sep .. br.loader.addonName .. sep .. "Settings" .. sep
else
- mainDir = wowDir .. sep .. br.addonName .. sep .. "Settings" .. sep
+ mainDir = wowDir .. sep .. br.loader.addonName .. sep .. "Settings" .. sep
end
-- Set Folder to Specified Folder if any
@@ -59,7 +336,7 @@ function br:checkDirectories(folder, class, spec, profile, instance)
-- Set the Spec Directory
if spec == nil then
- spec = br.selectedSpec
+ spec = br.loader.selectedSpec
end
if spec == nil then
spec = "Initial"
@@ -69,7 +346,7 @@ function br:checkDirectories(folder, class, spec, profile, instance)
-- Set the Profile Directory
if profile == nil then
- profile = br.selectedProfileName
+ profile = br.loader.selectedProfileName
end
local profileDir = specDir .. profile .. sep
checkDirectory(profileDir)
@@ -85,53 +362,240 @@ function br:checkDirectories(folder, class, spec, profile, instance)
return nil
end
-function br.deepcopy(orig)
+function settingsManagement:deepcopy(orig)
local orig_type = type(orig)
local copy
if orig_type == "table" then
copy = {}
for orig_key, orig_value in next, orig, nil do
- copy[br.deepcopy(orig_key)] = br.deepcopy(orig_value)
+ copy[settingsManagement:deepcopy(orig_key)] = settingsManagement:deepcopy(orig_value)
end
- setmetatable(copy, br.deepcopy(getmetatable(orig)))
+ setmetatable(copy, settingsManagement:deepcopy(getmetatable(orig)))
else -- number, string, boolean, etc
copy = orig
end
return copy
end
-function br:cleanSettings()
+-- Build all UI window pages programmatically for cleanup purposes
+-- This forces all pages to register in br.data.ui by temporarily setting the page dropdown
+function settingsManagement:buildAllUIPages()
+ if not br.ui or not br.ui.window then
+ return false
+ end
+
+ local totalPagesBuilt = 0
+ local failedPages = {}
+
+ -- Iterate through all windows in br.ui.window
+ for windowName, window in pairs(br.ui.window) do
+ if type(window) == "table" and window.pages and window.parent then
+ local pagesBuilt = 0
+ local pageDD = window.pageDD
+
+ if pageDD then
+ -- Save the current page index so we can restore it later
+ local originalPageIdx = pageDD.value or 1
+
+ for i = 1, #window.pages do
+ local page = window.pages[i]
+ local pageFunction = page[2]
+
+ if type(pageFunction) == "function" then
+ -- Set the page dropdown to this page index so activePage resolves correctly
+ pageDD.value = i
+
+ -- Mirror normal page switching behavior to prevent UI from accumulating
+ -- elements from multiple pages in the currently visible window.
+ if window.ReleaseChildren then
+ window:ReleaseChildren()
+ end
+
+ -- Call the page function to create all UI elements
+ local success, err = pcall(pageFunction)
+ if success then
+ pagesBuilt = pagesBuilt + 1
+ else
+ table.insert(failedPages, page[1] .. " (" .. tostring(err):sub(1, 50) .. ")")
+ end
+ end
+ end
+
+ -- Restore and rebuild original page so the window doesn't show a merged UI.
+ pageDD.value = originalPageIdx
+ if window.ReleaseChildren then
+ window:ReleaseChildren()
+ end
+ if window.pages[originalPageIdx] and type(window.pages[originalPageIdx][2]) == "function" then
+ pcall(window.pages[originalPageIdx][2])
+ end
+ else
+ -- Fallback for windows without pageDD - just try calling functions directly
+ for i = 1, #window.pages do
+ local page = window.pages[i]
+ local pageFunction = page[2]
+ if type(pageFunction) == "function" then
+ if window.ReleaseChildren then
+ window:ReleaseChildren()
+ end
+ local success, err = pcall(pageFunction)
+ if success then pagesBuilt = pagesBuilt + 1 end
+ end
+ end
+ end
+
+ if pagesBuilt > 0 then
+ totalPagesBuilt = totalPagesBuilt + pagesBuilt
+ end
+ end
+ end
+
+ if totalPagesBuilt > 0 then
+ -- br._G.print("Loading UI (" .. totalPagesBuilt .. " pages)...")
+ if #failedPages > 0 then
+ br._G.print("|cffFF8800[buildAllUIPages] " .. #failedPages .. " pages failed to build|r")
+ for _, failMsg in ipairs(failedPages) do
+ br._G.print("|cffFF8800 - " .. failMsg .. "|r")
+ end
+ end
+ return true
+ end
+
+ return false
+end
+
+function settingsManagement:cleanSettings()
if br.data and br.data.settings then
- if br.data.settings[br.selectedSpec] and br.data.settings[br.selectedSpec][br.selectedProfile] then
- local settings = br.data.settings[br.selectedSpec][br.selectedProfile]
- for page, _ in pairs(settings) do
- if type(page) == "table" then
- for option, _ in pairs(page) do
- if br.data.ui[page][option] == nil then
- print("Option: " ..
- tostring(option) ..
- " was not created has been removed from Page: " .. tostring(page) .. ".")
- settings[page][option] = nil
+ -- Check if Power toggle is off - if so, skip cleaning as UI was never built
+ if br.data.settings[br.loader.selectedSpec] and br.data.settings[br.loader.selectedSpec].toggles and
+ br.data.settings[br.loader.selectedSpec].toggles["Power"] ~= 1 then
+ br._G.print("|cffFF8800[cleanSettings] System toggled off - skipping cleanup|r")
+ return
+ end
+
+ -- Force all window pages to be created so we have complete UI registry
+ -- This is called here in case cleanSettings is invoked manually (e.g., before export)
+ -- If already built during loadSettings, this will be very fast
+ -- settingsManagement:buildAllUIPages()
+
+ -- Check if br.data.ui exists and has actual page data before attempting cleanup
+ -- This prevents cleaning when UI hasn't been fully initialized yet
+ if not br.data.ui then
+ br._G.print("|cffFF8800[cleanSettings] br.data.ui is nil - skipping cleanup|r")
+ return
+ end
+
+ -- Count how many pages exist in br.data.ui (excluding meta keys like PageList, currentPage, totalPages)
+ local uiPageCount = 0
+ for pageName, pageData in pairs(br.data.ui) do
+ if type(pageData) == "table" and pageName ~= "PageList" and pageName ~= "currentPage" and pageName ~= "totalPages" then
+ uiPageCount = uiPageCount + 1
+ end
+ end
+
+ -- If no actual pages exist in br.data.ui, skip cleaning
+ if uiPageCount == 0 then
+ br._G.print("|cffFF8800[cleanSettings] No UI pages found (count: " .. uiPageCount .. ") - skipping cleanup|r")
+ return
+ end
+
+ if uiPageCount >= 3 then
+ local settings = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]
+ local removedItems = {}
+
+ -- ========================================
+ -- PHASE 1: Remove duplicate/misplaced options
+ -- ========================================
+ -- Build a map of where each option SHOULD exist (from br.data.ui which reflects actual UI)
+ local optionToCorrectPage = {}
+ for page, pageData in pairs(br.data.ui) do
+ if type(pageData) == "table" and page ~= "PageList" and page ~= "currentPage" and page ~= "totalPages" then
+ for option, _ in pairs(pageData) do
+ if type(option) == "string" then
+ optionToCorrectPage[option] = page
+ end
+ end
+ end
+ end
+
+ -- Scan all pages in saved settings and remove options that belong on a different page
+ for page, pageData in pairs(settings) do
+ if type(pageData) == "table" and page ~= "PageList" and page ~= "currentPage" and page ~= "totalPages" then
+ for option, _ in pairs(pageData) do
+ -- Skip sections - they're UI elements, not settings
+ if not option:match("Section$") then
+ local correctPage = optionToCorrectPage[option]
+ -- If this option exists in UI but on a DIFFERENT page, remove it from here
+ -- Only if we actually found where it should be
+ if correctPage and type(correctPage) == "string" and correctPage ~= page then
+ settings[page][option] = nil
+ local cleanName = option:gsub(" Check", ""):gsub(" Drop", ""):gsub(" Status", "")
+ table.insert(removedItems, cleanName .. " (misplaced on " .. page .. ", should be on " .. correctPage .. ")")
+ end
+ end
+ end
+ end
+ end
+ else
+ br._G.print("|cffFFDD00[cleanSettings] Skipping duplicate check - only " .. uiPageCount .. " pages built (need 3+)|r")
+ end
+
+ if br.data.settings[br.loader.selectedSpec] and br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile] then
+ local settings = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]
+---@diagnostic disable-next-line: undefined-global
+ local removedItems = removedItems or {}
+
+ -- ========================================
+ -- PHASE 2: Remove orphaned options
+ -- ========================================
+ for page, pageData in pairs(settings) do
+ if type(pageData) == "table" and type(page) == "string" then
+ -- Check if this is an actual page (not PageList, currentPage, etc.)
+ if page ~= "PageList" and page ~= "currentPage" and page ~= "totalPages" then
+ -- Check if this page exists in br.data.ui
+ if not br.data.ui[page] then
+ -- Page doesn't exist in UI registry - this might be normal if page hasn't been created yet
+ -- Don't wipe settings for pages that haven't been visited/created
+ -- br._G.print("|cffFFDD00[cleanSettings] Skipping page '" .. page .. "' - not in UI registry (not created yet)|r")
+ else
+ -- Page exists in UI, check individual options
+ for option, _ in pairs(pageData) do
+ -- Skip sections - they're UI elements, not settings
+ if not option:match("Section$") then
+ -- Check if this option exists in br.data.ui for THIS specific page
+ if br.data.ui[page][option] == nil then
+ settings[page][option] = nil
+ local cleanName = option:gsub(" Check", ""):gsub(" Drop", ""):gsub(" Status", "")
+ table.insert(removedItems, cleanName .. " (orphaned from " .. page .. ")")
+ end
+ end
+ end
end
end
elseif type(page) == "string" then
+ -- This handles settings that aren't in a page structure
local option = page
- if br.data.ui[option] == nil then
- print("Option: " ..
- tostring(option) ..
- " was not created and had been removed.")
+ -- Skip sections - they're UI elements, not settings
+ if not option:match("Section$") and br.data.ui[option] == nil then
settings[option] = nil
+ table.insert(removedItems, option)
end
end
end
+
+ if #removedItems > 0 then
+ br._G.print("|cffFFDD00Settings Cleanup: Removed " .. #removedItems .. " duplicate/orphaned setting(s)|r")
+ -- Save immediately so cleaned settings persist
+ br.data.collectGarbage = false -- Prevent re-running cleanup
+ settingsManagement:saveSettings(nil, nil, br.loader.selectedSpec, br.loader.selectedProfileName)
+ end
+ br._G.print("UI Load Complete")
end
end
-end
-
--- Load Settings
-function br:loadSettings(folder, class, spec, profile, instance)
+end-- Load Settings
+function settingsManagement:loadSettings(folder, class, spec, profile, instance)
if br.unlocked and (not br.data.loadedSettings or br.rotationChanged) then
- local loadDir = br:checkDirectories(folder, class, spec, profile, instance)
+ local loadDir = settingsManagement:checkDirectories(folder, class, spec, profile, instance)
if not loadDir then
br._G.print("No settings directory found for " .. profile .. "!")
return
@@ -141,40 +605,43 @@ function br:loadSettings(folder, class, spec, profile, instance)
local fileFound = false
local profileFound = false
-- Load Settings
- if br:findFileInFolder("savedSettings.lua", loadDir) then
+ if settingsManagement:findFileInFolder("savedSettings.lua", loadDir) then
--br._G.print("Loading Settings File From Directory: " .. loadDir)
- brdata = br.tableLoad(loadDir .. "savedSettings.lua")
+ brdata = tableLoad(loadDir .. "savedSettings.lua")
if brdata then
fileFound = true
end
end
-- Load Profile
- if br:findFileInFolder("savedProfile.lua", loadDir) then
+ if settingsManagement:findFileInFolder("savedProfile.lua", loadDir) then
-- Print("Loading Saved Profiles")
-- Print("From Directory: "..loadDir)
- brprofile = br.tableLoad(loadDir .. "savedProfile.lua")
+ brprofile = tableLoad(loadDir .. "savedProfile.lua")
if brprofile then
profileFound = true
end
end
if fileFound then
br.ui:closeWindow("all")
- br.data = br.deepcopy(brdata)
+ br.data = settingsManagement:deepcopy(brdata)
+ -- Clear br.data.ui from loaded settings - it should NOT be loaded from disk
+ -- It will be rebuilt when UI is created and populated as pages are visited
+ br.data.ui = nil
if profileFound then
- br.profile = br.deepcopy(brprofile)
+ settingsManagement.profile = settingsManagement:deepcopy(brprofile)
end
br._G.print("Loaded Settings for Profile " .. tostring(profile))
- -- br:cleanSettings()
end
if not fileFound then
- if br.selectedProfileName ~= "None" then
+ if br.loader.selectedProfileName ~= "None" then
br._G.print("No File Called 'savedSettings.lua' Found In " .. loadDir)
else
br._G.print("No pre-existing settings to load.")
end
end
+
if spec == nil then
- spec = br.selectedSpec
+ spec = br.loader.selectedSpec
end
if spec == "" then
spec = "Initial"
@@ -191,45 +658,77 @@ function br:loadSettings(folder, class, spec, profile, instance)
br.ui:toggleWindow("config")
br.data.settings[spec].config.initialLoad = true
end
+
+ -- After config window is created and toggled, build all pages and run cleanup
+ -- By setting pageDD.value before calling each page function, the pages will
+ -- properly register in br.data.ui, allowing cleanup to detect duplicates/orphans
+ if fileFound then
+ br._G.C_Timer.After(1.0, function()
+ -- Ensure config window exists and is shown before building pages
+ if br.ui.window.config and br.ui.window.config.frame then
+ if not br.ui.window.config.frame:IsShown() then
+ br.ui:toggleWindow("config")
+ end
+ else
+ -- Window doesn't exist yet, create and show it
+ if br.ui.window.config == nil then br.ui.window.config = {} end
+ if br.ui.window.config.frame == nil then br.ui:createConfigWindow() end
+ br.ui:toggleWindow("config")
+ end
+
+ if settingsManagement:buildAllUIPages() then
+ settingsManagement:cleanSettings()
+ end
+
+ -- Close the window if it was initially closed
+ if initialLoad then
+ br.ui:closeWindow("config")
+ end
+ end)
+ end
+
br.data.loadedSettings = true
end
end
-- Save Settings
-function br:saveSettings(folder, class, spec, profile, instance, wipe)
- local saveDir = br:checkDirectories(folder, class, spec, profile, instance)
+function settingsManagement:saveSettings(folder, class, spec, profile, instance, wipe)
+ local saveDir = settingsManagement:checkDirectories(folder, class, spec, profile, instance)
if not saveDir then
br._G.print("No settings directory found for " .. profile .. "!")
return
end
- local brdata = wipe and {} or br.deepcopy(br.data)
- local brprofile = wipe and {} or br.deepcopy(br.profile)
+ -- if spec then
+ -- cleanOrphanedSettings(spec)
+ -- end
+ local brdata = wipe and {} or settingsManagement:deepcopy(br.data)
+ local brprofile = wipe and {} or settingsManagement:deepcopy(settingsManagement.profile)
br._G.print("Saving Profiles and Settings to Directory: " .. tostring(saveDir))
-- Save Files
- br.tableSave(brdata, saveDir .. "savedSettings.lua")
- br.tableSave(brprofile, saveDir .. "savedProfile.lua")
+ tableSave(brdata, saveDir .. "savedSettings.lua")
+ tableSave(brprofile, saveDir .. "savedProfile.lua")
br._G.print("Saved Settings for Profile " .. profile)
end
-br.fileList = {}
-function br:findFileInFolder(file, folder)
+settingsManagement.fileList = {}
+function settingsManagement:findFileInFolder(file, folder)
if folder == nil or folder == "" then return false end
- table.wipe(br.fileList)
- br.fileList = br._G.GetDirectoryFiles(folder .. "*.lua")
- for i = 1, #br.fileList do
- if br.fileList[i] == file then
+ table.wipe(settingsManagement.fileList)
+ settingsManagement.fileList = br._G.GetDirectoryFiles(folder .. "*.lua")
+ for i = 1, #settingsManagement.fileList do
+ if settingsManagement.fileList[i] == file then
return true
end
end
return false
end
-function br:loadLastProfileTracker()
- local loadDir = br:checkDirectories(nil, nil, nil, "Tracker")
- local selectedProfile = br.selectedSpec
- if br:findFileInFolder("lastProfileTracker.lua", loadDir) then
- local tracker = br.tableLoad(loadDir .. "lastProfileTracker.lua")
- local specID = br._G.GetSpecializationInfo(br._G.GetSpecialization())
+function settingsManagement:loadLastProfileTracker()
+ local loadDir = settingsManagement:checkDirectories(nil, nil, nil, "Tracker")
+ local selectedProfile = br.loader.selectedSpec
+ if settingsManagement:findFileInFolder("lastProfileTracker.lua", loadDir) then
+ local tracker = tableLoad(loadDir .. "lastProfileTracker.lua")
+ local specID = br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization())
if br.data == nil then
br.data = {}
end
@@ -246,27 +745,32 @@ function br:loadLastProfileTracker()
br.data.tracker[selectedProfile] = {}
end
local rotationFound = false
- local trackerName = br.data.tracker[br.selectedSpec]["RotationDropValue"]
- local specSettings = br.data.settings[br.selectedSpec]
- if not br.rotations[specID] then
- print("No rotations found for specID " .. specID)
+ local trackerName = br.data.tracker[br.loader.selectedSpec]["RotationDropValue"]
+ local specSettings = br.data.settings[br.loader.selectedSpec]
+ -- If rotations are not yet available, print once and mark to avoid repeated retries
+ if not br.loader.rotations[specID] then
+ br.loader.noRotationsFound = br.loader.noRotationsFound or {}
+ if not br.loader.noRotationsFound[specID] then
+ print("No rotations found for specID " .. specID)
+ br.loader.noRotationsFound[specID] = true
+ end
return
end
if trackerName then
- for i = 1, #br.rotations[specID] do
- if br.rotations[specID][i].name == trackerName then
- br.selectedProfileName = trackerName
+ for i = 1, #br.loader.rotations[specID] do
+ if br.loader.rotations[specID][i].name == trackerName then
+ br.loader.selectedProfileName = trackerName
specSettings["RotationDropValue"] = trackerName
specSettings["RotationDrop"] = i
rotationFound = true
- br._G.print("Found Last Used From Tracker: " .. trackerName)
+ -- br._G.print("Found Last Used From Tracker: " .. trackerName)
break
end
end
end
if not rotationFound then
- br._G.print("No Matching Rotation Found In Tracker, Defaulting to " .. br.rotations[specID][1].name)
- specSettings["RotationDropValue"] = br.rotations[specID][1].name
+ br._G.print("No Matching Rotation Found In Tracker, Defaulting to " .. br.loader.rotations[specID][1].name)
+ specSettings["RotationDropValue"] = br.loader.rotations[specID][1].name
specSettings["RotationDrop"] = 1
end
-- br._G.print("Tracker Load - Last Profile: " .. tostring(br.data.settings[selectedProfile]["RotationDrop"]))
@@ -274,51 +778,52 @@ function br:loadLastProfileTracker()
else
br._G.print("No Tracker found for " .. selectedProfile .. "! Creating Tracker....")
end
- br:saveLastProfileTracker()
+ settingsManagement:saveLastProfileTracker()
end
-function br:saveLastProfileTracker()
- local saveDir = br:checkDirectories(nil, nil, nil, "Tracker")
- local specID = br._G.GetSpecializationInfo(br._G.GetSpecialization()) or br.selectedSpecID
- if br.data ~= nil and br.data.settings ~= nil and br.data.settings[br.selectedSpec] ~= nil then
+function settingsManagement:saveLastProfileTracker()
+ local saveDir = settingsManagement:checkDirectories(nil, nil, nil, "Tracker")
+ local specID = br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization()) or br.loader.selectedSpecID
+ if specID == 0 and br.loader.selectedSpecID ~= nil then specID = br.loader.selectedSpecID end
+ if br.data ~= nil and br.data.settings ~= nil and br.data.settings[br.loader.selectedSpec] ~= nil and specID ~= 0 then
if br.data.tracker ~= nil then
- if br.data.tracker[br.selectedSpec] == nil then
- br.data.tracker[br.selectedSpec] = {}
+ if br.data.tracker[br.loader.selectedSpec] == nil then
+ br.data.tracker[br.loader.selectedSpec] = {}
end
- if br.data.settings[br.selectedSpec]["RotationDropValue"] then
+ if br.data.settings[br.loader.selectedSpec]["RotationDropValue"] then
local rotationFound = false
- for i = 1, #br.rotations[specID] do
- if br.rotations[specID][i].name == br.data.settings[br.selectedSpec]["RotationDropValue"] then
- br.data.tracker[br.selectedSpec]["RotationDropValue"] = br.data.settings[br.selectedSpec]
+ for i = 1, #br.loader.rotations[specID] do
+ if br.loader.rotations[specID][i].name == br.data.settings[br.loader.selectedSpec]["RotationDropValue"] then
+ br.data.tracker[br.loader.selectedSpec]["RotationDropValue"] = br.data.settings[br.loader.selectedSpec]
["RotationDropValue"]
- br.data.tracker[br.selectedSpec]["RotationDrop"] = i
+ br.data.tracker[br.loader.selectedSpec]["RotationDrop"] = i
rotationFound = true
break
end
end
if not rotationFound then
- br.data.tracker[br.selectedSpec]["RotationDropValue"] = br.rotations[specID][1].name
- br.data.tracker[br.selectedSpec]["RotationDrop"] = 1
+ br.data.tracker[br.loader.selectedSpec]["RotationDropValue"] = br.loader.rotations[specID][1].name
+ br.data.tracker[br.loader.selectedSpec]["RotationDrop"] = 1
end
- elseif br.rotations and br.rotations[specID] and br.rotations[specID][1] and br.rotations[specID][1].name ~= nil then
- br.data.settings[br.selectedSpec]["RotationDropValue"] = br.rotations[specID][1].name
- br.data.tracker[br.selectedSpec]["RotationDropValue"] = br.rotations[specID][1].name
- br.data.settings[br.selectedSpec]["RotationDrop"] = 1
- br.data.tracker[br.selectedSpec]["RotationDrop"] = 1
+ elseif br.loader.rotations and br.loader.rotations[specID] and br.loader.rotations[specID][1] and br.loader.rotations[specID][1].name ~= nil then
+ br.data.settings[br.loader.selectedSpec]["RotationDropValue"] = br.loader.rotations[specID][1].name
+ br.data.tracker[br.loader.selectedSpec]["RotationDropValue"] = br.loader.rotations[specID][1].name
+ br.data.settings[br.loader.selectedSpec]["RotationDrop"] = 1
+ br.data.tracker[br.loader.selectedSpec]["RotationDrop"] = 1
else
- br.data.settings[br.selectedSpec]["RotationDropValue"] = ""
- br.data.tracker[br.selectedSpec]["RotationDropValue"] = ""
- br.data.settings[br.selectedSpec]["RotationDrop"] = 1
- br.data.tracker[br.selectedSpec]["RotationDrop"] = 1
+ br.data.settings[br.loader.selectedSpec]["RotationDropValue"] = ""
+ br.data.tracker[br.loader.selectedSpec]["RotationDropValue"] = ""
+ br.data.settings[br.loader.selectedSpec]["RotationDrop"] = 1
+ br.data.tracker[br.loader.selectedSpec]["RotationDrop"] = 1
end
end
- else
+ elseif specID ~= 0 then
br._G.print("Nothing found, recreating tracker")
br.data.tracker = {}
- br.data.tracker[br.selectedSpec] = {}
- br.data.tracker[br.selectedSpec]["RotationDrop"] = 1
- br.data.tracker[br.selectedSpec]["RotationDropValue"] = br.rotations[specID][1].name
+ br.data.tracker[br.loader.selectedSpec] = {}
+ br.data.tracker[br.loader.selectedSpec]["RotationDrop"] = 1
+ br.data.tracker[br.loader.selectedSpec]["RotationDropValue"] = br.loader.rotations[specID][1].name
end
- br._G.print("Saving Tracker to Directory: " .. tostring(saveDir))
- br.tableSave(br.data.tracker, saveDir .. "lastProfileTracker.lua")
+ -- br._G.print("Saving Tracker to Directory: " .. tostring(saveDir))
+ tableSave(br.data.tracker, saveDir .. "lastProfileTracker.lua")
end
diff --git a/System/UI/Toggles/ToggleFunctions.lua b/System/UI/Toggles/ToggleFunctions.lua
index 807edf64ef..2694ffdb55 100644
--- a/System/UI/Toggles/ToggleFunctions.lua
+++ b/System/UI/Toggles/ToggleFunctions.lua
@@ -1,54 +1,64 @@
local _, br = ...
+br.ui.toggles = br.ui.toggles or {}
+
+local function StripModes(name)
+ return name:gsub("Modes$", "")
+end
+
+local function Trim(s)
+ return s:match("^%s*(.-)%s*$")
+end
+
+local function FindToggle(toggleValue)
+ local toggles = br.data.settings[br.loader.selectedSpec].toggles
+ for k, _ in pairs(toggles) do
+ if string.upper(toggleValue) == string.upper(k) then
+ return k
+ end
+ end
+ return nil
+end
+
function br.ui:createToggle(table, name, col, row)
if type(name) ~= "string" then
br._G.print("Invaild type " .. type(name) .. " detected for table " .. name .. ". Please let devs know!")
else
- br[name .. "Modes"] = table
- br.CreateButton(name, col, row)
+ br.ui.toggles[name] = table
+ br.ui:CreateButton(name, col, row)
-- br.ui:RefreshButton(name)
end
end
function br.ui:RefreshButton(name)
- local index = br[name .. "Modes"] and br[name .. "Modes"].value or 1
- br.changeButton(name, index)
+ local index = br.ui.toggles[name] and br.ui.toggles[name].value or 1
+ br.ui:changeButton(name, index)
end
-- when we find a match, we reset tooltip
-function br.ResetTip(toggleValue, thisValue)
- br._G.GameTooltip:SetOwner(br["button" .. toggleValue], br.mainButton, 0, 0)
- br._G.GameTooltip:SetText(br[toggleValue .. "Modes"][thisValue].tip, 225 / 255, 225 / 255, 225 / 255, nil, true)
+function br.ui:ResetTip(toggleValue, thisValue)
+ br._G.GameTooltip:SetOwner(br.ui.toggles["button" .. StripModes(toggleValue)], br.ui.toggles.mainButton, 0, 0)
+ br._G.GameTooltip:SetText(br.ui.toggles[toggleValue][thisValue].tip, 225 / 255, 225 / 255, 225 / 255, nil, true)
br._G.GameTooltip:Show()
end
-function br.GarbageButtons()
- if br.buttonsTable and not br._G.UnitAffectingCombat("player") then
- for i = 1, #br.buttonsTable do
- local Name = br.buttonsTable[i].name
- if br["button" .. Name] then br["button" .. Name]:Hide() end
- if br["text" .. Name] then br["text" .. Name]:Hide() end
- if br["frame" .. Name] then br["frame" .. Name].texture:Hide() end
- br[Name .. "Modes"] = nil
+function br.ui:GarbageButtons()
+ if br.ui.toggles.buttonsTable and not br._G.UnitAffectingCombat("player") then
+ for i = 1, #br.ui.toggles.buttonsTable do
+ local Name = br.ui.toggles.buttonsTable[i].name
+ if br.ui.toggles["button" .. Name] then br.ui.toggles["button" .. Name]:Hide() end
+ if br.ui.toggles["text" .. Name] then br.ui.toggles["text" .. Name]:Hide() end
+ if br.ui.toggles["frame" .. Name] then br.ui.toggles["frame" .. Name].texture:Hide() end
+ br.ui.toggles[Name] = nil
end
end
end
-local function FindToggle(toggleValue)
- local toggles = br.data.settings[br.selectedSpec].toggles
- for k, _ in pairs(toggles) do
- if string.upper(toggleValue) == string.upper(k) then
- return k
- end
- end
- return nil
-end
-
-function br.ToggleToValue(toggleValue, index)
- if br[toggleValue .. "Modes"] == nil then
+function br.ui:ToggleToValue(toggleValue, index)
+ if br.ui.toggles[toggleValue] == nil then
toggleValue = FindToggle(toggleValue)
end
index = tonumber(index)
- local modesCount = #br[toggleValue .. "Modes"]
+ local modesCount = #br.ui.toggles[toggleValue]
if index > modesCount then
br._G.print(
"Invalid Toggle Index for |cffFFDD11" ..
@@ -56,37 +66,38 @@ function br.ToggleToValue(toggleValue, index)
": |cFFFF0000 Index ( |r" .. index .. "|cFFFF0000) exceeds Max ( |r" .. modesCount .. "|cFFFF0000)|r."
)
else
- br.specialToggleCodes(toggleValue, index)
- if not br.data.settings[br.selectedSpec].toggles then
- br.data.settings[br.selectedSpec].toggles = {}
+ br.ui:specialToggleCodes(toggleValue, index)
+ if not br.data.settings[br.loader.selectedSpec].toggles then
+ br.data.settings[br.loader.selectedSpec].toggles = {}
end
- br.data.settings[br.selectedSpec].toggles[tostring(toggleValue)] = index
- br.changeButton(toggleValue, index)
+ br.data.settings[br.loader.selectedSpec].toggles[tostring(toggleValue)] = index
+ br.ui:changeButton(toggleValue, index)
-- We reset the tip
- br.ResetTip(toggleValue, index)
+ br.ui:ResetTip(toggleValue, index)
end
end
-function br.ToggleValue(toggleValue)
- if br[toggleValue .. "Modes"] == nil then
+function br.ui:ToggleValue(toggleValue)
+ local toggles = br.data.settings[br.loader.selectedSpec].toggles
+ if toggles[toggleValue] == nil then
toggleValue = FindToggle(toggleValue)
end
- if br[toggleValue .. "Modes"] == nil then
+ if toggles[toggleValue] == nil then
br._G.print("No toggle mode found for " .. toggleValue .. ". Please inform devs of this error!")
return
end
-- prevent nil fails
local toggleOldValue
- if br.data.settings[br.selectedSpec].toggles then
- toggleOldValue = br.data.settings[br.selectedSpec].toggles[tostring(toggleValue)] or 1
+ if toggles then
+ toggleOldValue = toggles[tostring(toggleValue)] or 1
end
- local modesCount = #br[toggleValue .. "Modes"]
+ local modesCount = #br.ui.toggles[toggleValue]
if toggleOldValue == nil then
- if not br.data.settings[br.selectedSpec].toggles then
- br.data.settings[br.selectedSpec].toggles = {}
+ if not toggles then
+ toggles = {}
end
- br.data.settings[br.selectedSpec].toggles[tostring(toggleValue)] = 1
- toggleOldValue = br.data.settings[br.selectedSpec].toggles[tostring(toggleValue)]
+ toggles[tostring(toggleValue)] = 1
+ toggleOldValue = toggles[tostring(toggleValue)]
end
-- Scan Table and find which mode = our i
for i = 1, modesCount do
@@ -95,217 +106,251 @@ function br.ToggleValue(toggleValue)
if modesCount > i then
-- calculate newValue
local newValue = i + 1
- br.specialToggleCodes(toggleValue, newValue)
+ br.ui:specialToggleCodes(toggleValue, newValue)
-- We set the value in DB
- br.data.settings[br.selectedSpec].toggles[tostring(toggleValue)] = newValue
- br.changeButton(toggleValue, newValue)
+ toggles[tostring(toggleValue)] = newValue
+ br.ui:changeButton(toggleValue, newValue)
-- We reset the tip
- br.ResetTip(toggleValue, newValue)
+ br.ui:ResetTip(toggleValue, newValue)
break
else
- br.specialToggleCodes(toggleValue, 1)
+ br.ui:specialToggleCodes(toggleValue, 1)
-- if cannot go higher we define mode to 1.
- br.data.settings[br.selectedSpec].toggles[tostring(toggleValue)] = 1
- br.changeButton(toggleValue, 1)
+ toggles[tostring(toggleValue)] = 1
+ br.ui:changeButton(toggleValue, 1)
-- We reset the tip
- br.ResetTip(toggleValue, 1)
+ br.ui:ResetTip(toggleValue, 1)
break
end
end
end
end
-function br.ToggleMinus(toggleValue)
- if br[toggleValue .. "Modes"] == nil then
+function br.ui:ToggleMinus(toggleValue)
+ local toggles = br.data.settings[br.loader.selectedSpec].toggles
+ if not toggles[tostring(toggleValue)] then
toggleValue = FindToggle(toggleValue)
end
- if br[toggleValue .. "Modes"] == nil then
+ if not toggles[tostring(toggleValue)] then
br._G.print("No toggle mode found for " .. toggleValue .. ". Please inform devs of this error!")
return
end
-- prevent nil fails
- if not br.data.settings[br.selectedSpec].toggles then
- br.data.settings[br.selectedSpec].toggles = {}
+ if not toggles then
+ toggles = {}
end
- if br.data.settings[br.selectedSpec].toggles[tostring(toggleValue)] == nil then
- br.data.settings[br.selectedSpec].toggles[tostring(toggleValue)] = 1
+ if toggles[tostring(toggleValue)] == nil then
+ toggles[tostring(toggleValue)] = 1
end
- local modesCount = #br[toggleValue .. "Modes"]
+ local modesCount = #br.ui.toggles[tostring(toggleValue)]
-- Scan Table and find which mode = our i
for i = 1, modesCount do
- local thisValue = br.data.settings[br.selectedSpec].toggles[tostring(toggleValue)] or 1
+ local thisValue = toggles[tostring(toggleValue)] or 1
if thisValue == i then
-- see if we can go lower in modes
local newValue = i - 1
if i > 1 then
-- calculate newValue
- br.specialToggleCodes(toggleValue, newValue)
+ br.ui:specialToggleCodes(toggleValue, newValue)
-- We set the value in DB
- br.data.settings[br.selectedSpec].toggles[tostring(toggleValue)] = newValue
+ toggles[tostring(toggleValue)] = newValue
-- change the button
- br.changeButton(toggleValue, newValue)
+ br.ui:changeButton(toggleValue, newValue)
-- We reset the tip
- br.ResetTip(toggleValue, newValue)
+ br.ui:ResetTip(toggleValue, newValue)
break
else
-- if cannot go lower we define to last mode
- br.data.settings[br.selectedSpec].toggles[tostring(toggleValue)] = modesCount
- br.specialToggleCodes(toggleValue, modesCount)
- br.changeButton(toggleValue, modesCount)
+ toggles[tostring(toggleValue)] = modesCount
+ br.ui:specialToggleCodes(toggleValue, modesCount)
+ br.ui:changeButton(toggleValue, modesCount)
-- We reset the tip
- br.ResetTip(toggleValue, modesCount)
+ br.ui:ResetTip(toggleValue, modesCount)
break
end
end
end
end
-function br.specialToggleCodes(toggleValue, newValue)
+function br.ui:specialToggleCodes(toggleValue, newValue)
if toggleValue == "Interrupts" then
local InterruptsModes = br.InterruptsModes
if newValue == 1 and InterruptsModes[1].mode == "None" then
-- no interupts mode
- if br.data.settings[br.selectedSpec]["Interrupts HandlerCheck"] ~= 0 then
+ if br.data.settings[br.loader.selectedSpec]["Interrupts HandlerCheck"] ~= 0 then
br["optionsInterrupts HandlerCheck"]:Click()
end
elseif newValue == 2 and InterruptsModes[2].mode == "Raid" then
-- on/off switch
- if br.data.settings[br.selectedSpec]["Interrupts HandlerCheck"] ~= 1 then
+ if br.data.settings[br.loader.selectedSpec]["Interrupts HandlerCheck"] ~= 1 then
br["optionsInterrupts HandlerCheck"]:Click()
end
-- only known switch
- if br.data.settings[br.selectedSpec]["Only Known UnitsCheck"] ~= 1 then
+ if br.data.settings[br.loader.selectedSpec]["Only Known UnitsCheck"] ~= 1 then
br["optionsOnly Known UnitsCheck"]:Click()
end
elseif newValue == 3 and InterruptsModes[3].mode == "All" then
-- interrupt all mode
-- on/off switch
- if br.data.settings[br.selectedSpec]["Interrupts HandlerCheck"] ~= 1 then
+ if br.data.settings[br.loader.selectedSpec]["Interrupts HandlerCheck"] ~= 1 then
br["optionsInterrupts HandlerCheck"]:Click()
end
-- only known switch
- if br.data.settings[br.selectedSpec]["Only Known UnitsCheck"] ~= 0 then
+ if br.data.settings[br.loader.selectedSpec]["Only Known UnitsCheck"] ~= 0 then
br["optionsOnly Known UnitsCheck"]:Click()
end
end
end
end
-function br.changeButtonValue(toggleValue, newValue)
- if br[toggleValue .. "Modes"] == nil then
+function br.ui:changeButtonValue(toggleValue, newValue)
+ if br.ui.toggles[toggleValue] == nil then
toggleValue = FindToggle(toggleValue)
end
- if br[toggleValue .. "Modes"] == nil then
+ if br.ui.toggles[toggleValue] == nil then
br._G.print("No toggle mode found for " .. toggleValue .. ". Please inform devs of this error!")
return
end
- br.data.settings[br.selectedSpec].toggles[tostring(toggleValue)] = newValue
+ br.data.settings[br.loader.selectedSpec].toggles[tostring(toggleValue)] = newValue
end
--- set to desired button value
-function br.changeButton(toggleValue, newValue)
+ -- set to desired button value
+function br.ui:changeButton(toggleValue, newValue)
+ local toggleValue = Trim(toggleValue)
if newValue == 0 then newValue = 1 end
local Icon
-- define text
- br["text" .. toggleValue]:SetText(br[toggleValue .. "Modes"][newValue].mode)
+ br.ui.toggles["text" .. StripModes(toggleValue)]:SetText(br.ui.toggles[toggleValue][newValue].mode)
-- define icon
- if type(br[toggleValue .. "Modes"][newValue].icon) == "number" then
- Icon = select(3, br._G.GetSpellInfo(br[toggleValue .. "Modes"][newValue].icon))
+ local iconValue = br.ui.toggles[toggleValue][newValue].icon
+ -- Handle table-based spell ranks (Classic WoW)
+ if type(iconValue) == "table" then
+ -- Get highest known rank for icon display
+ if br.functions and br.functions.spell and br.functions.spell.getHighestKnownRank then
+ iconValue = br.functions.spell:getHighestKnownRank(iconValue) or iconValue[1]
+ else
+ iconValue = iconValue[1] -- Fallback to first rank
+ end
+ end
+ if type(iconValue) == "number" then
+ -- Try spell first, then item if spell doesn't exist
+ local spellName, _, spellIcon = br.api.wow.GetSpellInfo(iconValue)
+ if spellName then
+ Icon = spellIcon
+ else
+ -- Try as item ID
+ Icon = br._G.C_Item.GetItemIconByID(iconValue)
+ end
else
- Icon = br[toggleValue .. "Modes"][newValue].icon
+ Icon = iconValue
end
- br["button" .. toggleValue]:SetNormalTexture(Icon or br.emptyIcon)
+ br.ui.toggles["button" .. StripModes(toggleValue)]:SetNormalTexture(Icon or br.ui.toggles.emptyIcon)
-- define highlight
- if br[toggleValue .. "Modes"][newValue].highlight == 0 then
- br["frame" .. toggleValue].texture:SetTexture(br.genericIconOff)
+ if br.ui.toggles[toggleValue][newValue].highlight == 0 then
+ br.ui.toggles["frame" .. StripModes(toggleValue)].texture:SetTexture(br.ui.toggles.genericIconOff)
else
- br["frame" .. toggleValue].texture:SetTexture(br.genericIconOn)
+ br.ui.toggles["frame" .. StripModes(toggleValue)].texture:SetTexture(br.ui.toggles.genericIconOn)
end
-- We tell the user we changed mode
- br.ChatOverlay("\124cFF3BB0FF" .. br[toggleValue .. "Modes"][newValue].overlay)
+ br.ui.chatOverlay:Show("\124cFF3BB0FF" .. br.ui.toggles[toggleValue][newValue].overlay)
-- We reset the tip
- br.ResetTip(toggleValue, newValue)
+ br.ui:ResetTip(toggleValue, newValue)
end
-function br.UpdateButton(Name)
+function br.ui:UpdateButton(Name)
Name = tostring(Name) --string.upper(Name)
- br.ToggleValue(Name)
+ br.ui:ToggleValue(Name)
end
-function br.buttonsResize()
- for i = 1, #br.buttonsTable do
- local Name = br.buttonsTable[i].name
- local x = br.buttonsTable[i].bx
- local y = br.buttonsTable[i].by
- br["button" .. Name]:SetWidth(br.data.settings["buttonSize"])
- br["button" .. Name]:SetHeight(br.data.settings["buttonSize"])
- br["button" .. Name]:SetPoint("LEFT", x * (br.data.settings["buttonSize"]), y * (br.data.settings["buttonSize"]))
- br["text" .. Name]:SetTextHeight(br.data.settings["buttonSize"] / 3)
- br["text" .. Name]:SetPoint("CENTER", 0, -(br.data.settings["buttonSize"] / 4))
- br["frame" .. Name]:SetWidth(br.data.settings["buttonSize"] * 1.67)
- br["frame" .. Name]:SetHeight(br.data.settings["buttonSize"] * 1.67)
- br["frame" .. Name].texture:SetWidth(br.data.settings["buttonSize"] * 1.67)
- br["frame" .. Name].texture:SetHeight(br.data.settings["buttonSize"] * 1.67)
+function br.ui:buttonsResize()
+ for i = 1, #br.ui.toggles.buttonsTable do
+ local Name = br.ui.toggles.buttonsTable[i].name
+ local x = br.ui.toggles.buttonsTable[i].bx
+ local y = br.ui.toggles.buttonsTable[i].by
+ br.ui.toggles["button" .. Name]:SetWidth(br.data.settings["buttonSize"])
+ br.ui.toggles["button" .. Name]:SetHeight(br.data.settings["buttonSize"])
+ br.ui.toggles["button" .. Name]:SetPoint("LEFT", x * (br.data.settings["buttonSize"]), y * (br.data.settings["buttonSize"]))
+ br.ui.toggles["text" .. Name]:SetTextHeight(br.data.settings["buttonSize"] / 3)
+ br.ui.toggles["text" .. Name]:SetPoint("CENTER", 0, -(br.data.settings["buttonSize"] / 4))
+ br.ui.toggles["frame" .. Name]:SetWidth(br.data.settings["buttonSize"] * 1.67)
+ br.ui.toggles["frame" .. Name]:SetHeight(br.data.settings["buttonSize"] * 1.67)
+ br.ui.toggles["frame" .. Name].texture:SetWidth(br.data.settings["buttonSize"] * 1.67)
+ br.ui.toggles["frame" .. Name].texture:SetHeight(br.data.settings["buttonSize"] * 1.67)
end
end
-- /run CreateButton("AoE",2,2)
-function br.CreateButton(Name, x, y)
+function br.ui:CreateButton(Name, x, y)
if type(Name) ~= "string" then
br._G.print("Invaild type " .. type(Name) .. " detected for table " .. Name .. ". Please let devs know!")
return
end
- if br.data.settings[br.selectedSpec] ~= nil then
+ if br.data.settings[br.loader.selectedSpec] ~= nil then
local Icon
-- local Name = string.upper(Name)
-- todo: extend to use spec + profile specific variable; ATM it shares between profile AND spec, -> global for char
- local toggleIndex = br.data.settings[br.selectedSpec].toggles[Name]
- if toggleIndex == nil or toggleIndex > #br[Name .. "Modes"] or toggleIndex == 0 then
+ local toggleIndex = br.data.settings[br.loader.selectedSpec].toggles[Name]
+ if toggleIndex == nil or toggleIndex > #br.ui.toggles[Name] or toggleIndex == 0 then
toggleIndex = 1
end
- if br.buttonsTable then
- br._G.tinsert(br.buttonsTable, { name = Name, bx = x, by = y })
+ if br.ui.toggles.buttonsTable then
+ br._G.tinsert(br.ui.toggles.buttonsTable, { name = Name, bx = x, by = y })
end
- br["button" .. Name] = br._G.CreateFrame("Button", "MyButtonBR", br.mainButton, "SecureHandlerClickTemplate")
- br["button" .. Name]:SetWidth(br.data.settings["buttonSize"])
- br["button" .. Name]:SetHeight(br.data.settings["buttonSize"])
- br["button" .. Name]:SetPoint(
+ br.ui.toggles["button" .. Name] = br._G.CreateFrame("Button", "MyButtonBR", br.ui.toggles.mainButton, "SecureHandlerClickTemplate")
+ br.ui.toggles["button" .. Name]:SetWidth(br.data.settings["buttonSize"])
+ br.ui.toggles["button" .. Name]:SetHeight(br.data.settings["buttonSize"])
+ br.ui.toggles["button" .. Name]:SetPoint(
"LEFT",
x * (br.data.settings["buttonSize"]) + (x * 2),
y * (br.data.settings["buttonSize"]) + (y * 2)
)
- br["button" .. Name]:RegisterForClicks("AnyUp")
- local toggleIcon = br[Name .. "Modes"][toggleIndex].icon
+ br.ui.toggles["button" .. Name]:RegisterForClicks("AnyUp")
+ local toggleIcon = br.ui.toggles[Name][toggleIndex].icon
+ -- Handle table-based spell ranks (Classic WoW)
+ if type(toggleIcon) == "table" then
+ -- Get highest known rank for icon display
+ if br.functions and br.functions.spell and br.functions.spell.getHighestKnownRank then
+ toggleIcon = br.functions.spell:getHighestKnownRank(toggleIcon) or toggleIcon[1]
+ else
+ toggleIcon = toggleIcon[1] -- Fallback to first rank
+ end
+ end
if toggleIcon ~= nil and type(toggleIcon) == "number" then
- Icon = toggleIcon
+ -- Try spell first, then item if spell doesn't exist
+ local spellName, _, spellIcon = br.api.wow.GetSpellInfo(toggleIcon)
+ if spellName then
+ Icon = spellIcon
+ else
+ -- Try as item ID
+ Icon = br._G.C_Item.GetItemIconByID(toggleIcon)
+ end
else
- Icon = br.emptyIcon
+ Icon = toggleIcon or br.ui.toggles.emptyIcon
end
- local _, _, spellIcon = br._G.GetSpellInfo(Icon)
- Icon = spellIcon or br.emptyIcon
- br["button" .. Name]:SetNormalTexture(Icon)
+ Icon = Icon or br.ui.toggles.emptyIcon
+ br.ui.toggles["button" .. Name]:SetNormalTexture(Icon)
--CreateBorder(br["button"..Name], 8, 0.6, 0.6, 0.6)
- br["text" .. Name] = br["button" .. Name]:CreateFontString(nil, "OVERLAY")
- br["text" .. Name]:SetFont(br.data.settings.font, br.data.settings.fontsize, "THICKOUTLINE")
- br["text" .. Name]:SetJustifyH("CENTER")
- br["text" .. Name]:SetTextHeight(br.data.settings["buttonSize"] / 3)
- br["text" .. Name]:SetPoint("CENTER", 3, -(br.data.settings["buttonSize"] / 8))
- br["text" .. Name]:SetTextColor(1, 1, 1, 1)
- br["frame" .. Name] = br._G.CreateFrame("Frame", nil, br["button" .. Name])
- br["frame" .. Name]:SetWidth(br.data.settings["buttonSize"] * 1.67)
- br["frame" .. Name]:SetHeight(br.data.settings["buttonSize"] * 1.67)
- br["frame" .. Name]:SetPoint("CENTER")
- br["frame" .. Name].texture = br["frame" .. Name]:CreateTexture(nil, "OVERLAY")
- br["frame" .. Name].texture:SetAllPoints()
- br["frame" .. Name].texture:SetWidth(br.data.settings["buttonSize"] * 1.67)
- br["frame" .. Name].texture:SetHeight(br.data.settings["buttonSize"] * 1.67)
- br["frame" .. Name].texture:SetAlpha(1)
- br["frame" .. Name].texture:SetTexture(br.genericIconOn)
- if br[Name .. "Modes"] == nil then
+ br.ui.toggles["text" .. Name] = br.ui.toggles["button" .. Name]:CreateFontString(nil, "OVERLAY")
+ br.ui.toggles["text" .. Name]:SetFont(br.data.settings.font, br.data.settings.fontsize, "THICKOUTLINE")
+ br.ui.toggles["text" .. Name]:SetJustifyH("CENTER")
+ br.ui.toggles["text" .. Name]:SetTextHeight(br.data.settings["buttonSize"] / 3)
+ br.ui.toggles["text" .. Name]:SetPoint("CENTER", 3, -(br.data.settings["buttonSize"] / 8))
+ br.ui.toggles["text" .. Name]:SetTextColor(1, 1, 1, 1)
+ br.ui.toggles["frame" .. Name] = br._G.CreateFrame("Frame", nil, br.ui.toggles["button" .. Name])
+ br.ui.toggles["frame" .. Name]:SetWidth(br.data.settings["buttonSize"] * 1.67)
+ br.ui.toggles["frame" .. Name]:SetHeight(br.data.settings["buttonSize"] * 1.67)
+ br.ui.toggles["frame" .. Name]:SetPoint("CENTER")
+ br.ui.toggles["frame" .. Name].texture = br.ui.toggles["frame" .. Name]:CreateTexture(nil, "OVERLAY")
+ br.ui.toggles["frame" .. Name].texture:SetAllPoints()
+ br.ui.toggles["frame" .. Name].texture:SetWidth(br.data.settings["buttonSize"] * 1.67)
+ br.ui.toggles["frame" .. Name].texture:SetHeight(br.data.settings["buttonSize"] * 1.67)
+ br.ui.toggles["frame" .. Name].texture:SetAlpha(1)
+ br.ui.toggles["frame" .. Name].texture:SetTexture(br.ui.toggles.genericIconOn)
+ if br.ui.toggles[Name] == nil then
br._G.print("No table found for " .. Name)
- br[Name .. "Modes"] = tostring(Name)
+ br.ui.toggles[Name] = tostring(Name)
else
- br[Name .. "Modes"] = br[Name .. "Modes"] or ""
+ br.ui.toggles[Name] = br.ui.toggles[Name] or ""
end
local modeValue
if toggleIndex == nil then
@@ -314,23 +359,24 @@ function br.CreateButton(Name, x, y)
else
modeValue = toggleIndex
end
- br["button" .. Name]:SetScript(
+ br.data.settings[br.loader.selectedSpec].toggles[Name] = modeValue
+ br.ui.toggles["button" .. Name]:SetScript(
"OnClick",
function(_, button)
if button == "RightButton" then
- br.ToggleMinus(Name)
+ br.ui:ToggleMinus(Name)
else
- br.ToggleValue(Name)
+ br.ui:ToggleValue(Name)
end
end
)
- br["button" .. Name]:SetScript(
+ br.ui.toggles["button" .. Name]:SetScript(
"OnMouseWheel",
function(_, delta)
local Go = false
if delta < 0 and toggleIndex > 1 then
Go = true
- elseif delta > 0 and toggleIndex < #br[Name .. "Modes"] then
+ elseif delta > 0 and toggleIndex < #br.ui.toggles[Name] then
Go = true
end
if Go == true then
@@ -338,12 +384,12 @@ function br.CreateButton(Name, x, y)
end
end
)
- br["button" .. Name]:SetScript(
+ br.ui.toggles["button" .. Name]:SetScript(
"OnEnter",
function()
- br._G.GameTooltip:SetOwner(br["button" .. Name], br._G.UIParent, 0, 0)
+ br._G.GameTooltip:SetOwner(br.ui.toggles["button" .. Name], br._G.UIParent, 0, 0)
br._G.GameTooltip:SetText(
- br[Name .. "Modes"][toggleIndex].tip,
+ br.ui.toggles[Name][toggleIndex].tip,
225 / 255,
225 / 255,
225 / 255,
@@ -353,31 +399,31 @@ function br.CreateButton(Name, x, y)
br._G.GameTooltip:Show()
end
)
- br["button" .. Name]:SetScript(
+ br.ui.toggles["button" .. Name]:SetScript(
"OnLeave",
function()
br._G.GameTooltip:Hide()
end
)
- br["text" .. Name]:SetText(br[Name .. "Modes"][modeValue].mode)
- if br[Name .. "Modes"][modeValue].highlight == 0 then
- br["frame" .. Name].texture:SetTexture(br.genericIconOff)
+ br.ui.toggles["text" .. Name]:SetText(br.ui.toggles[Name][modeValue].mode)
+ if br.ui.toggles[Name][modeValue].highlight == 0 then
+ br.ui.toggles["frame" .. Name].texture:SetTexture(br.ui.toggles.genericIconOff)
else
- br["frame" .. Name].texture:SetTexture(br.genericIconOn)
+ br.ui.toggles["frame" .. Name].texture:SetTexture(br.ui.toggles.genericIconOn)
end
- if br.mainButton ~= nil then
- if br.data.settings[br.selectedSpec].toggles["Main"] == 1 and not br._G.UnitAffectingCombat("player") then
- br.mainButton:Show()
+ if br.ui.toggles.mainButton ~= nil then
+ if br.data.settings[br.loader.selectedSpec].toggles["Main"] == 1 and not br._G.UnitAffectingCombat("player") then
+ br.ui.toggles.mainButton:Show()
elseif not br._G.UnitAffectingCombat("player") then
- br.mainButton:Hide()
+ br.ui.toggles.mainButton:Hide()
elseif br._G.UnitAffectingCombat("player") then
br._G.print(
"Combat Lockdown detected. Unable to modify button bar. Please try again when out of combat.")
end
end
- br.SlashCommandHelp(
- "br toggle " .. Name .. " 1-" .. #br[Name .. "Modes"],
- "Toggles " .. Name .. " Modes, Optional: specify number to toggle to or 2 numbers to toggle between"
+ br.slashCommands:SlashCommandHelp(
+ "br toggle " .. Name .. " 1-" .. #br.ui.toggles[Name],
+ "Toggles " .. Name .. ", Optional: specify number to toggle to or 2 numbers to toggle between"
)
end
end
diff --git a/System/UI/Toggles/Toggles.lua b/System/UI/Toggles/Toggles.lua
index 2fd3d023e0..908585ecce 100644
--- a/System/UI/Toggles/Toggles.lua
+++ b/System/UI/Toggles/Toggles.lua
@@ -1,26 +1,28 @@
local _, br = ...
+br.ui.toggles = br.ui.toggles or {}
+local toggles = br.ui.toggles
-- this handles old profiles buttons
-function br.TogglesFrame()
- br.GarbageButtons()
+function toggles:TogglesFrame()
+ br.ui:GarbageButtons()
---------------------------
-- Main Button --
---------------------------
-- Default Values
- br.emptyIcon = [[Interface\FrameGeneral\UI-Background-Marble]]
- br.backIconOn = [[Interface\ICONS\Spell_Holy_PowerWordShield]]
- br.backIconOff = [[Interface\ICONS\SPELL_HOLY_DEVOTIONAURA]]
- br.genericIconOff = [[Interface\GLUES\CREDITS\Arakkoa1]]
- br.genericIconOn = [[Interface/BUTTONS/CheckButtonGlow]]
- br.buttonSize = br.data.settings["buttonSize"]
- br.buttonWidth = br.data.settings["buttonSize"]
- br.buttonHeight = br.data.settings["buttonSize"]
- if not br.mainButton then
- br.mainButton = br._G.CreateFrame("Button", "MyButtonBR", br._G.UIParent, "SecureHandlerClickTemplate")
+ toggles.emptyIcon = [[Interface\FrameGeneral\UI-Background-Marble]]
+ toggles.backIconOn = [[Interface\ICONS\Spell_Holy_PowerWordShield]]
+ toggles.backIconOff = [[Interface\ICONS\SPELL_HOLY_DEVOTIONAURA]]
+ toggles.genericIconOff = [[Interface\GLUES\CREDITS\Arakkoa1]]
+ toggles.genericIconOn = [[Interface/BUTTONS/CheckButtonGlow]]
+ toggles.buttonSize = br.data.settings["buttonSize"]
+ toggles.buttonWidth = br.data.settings["buttonSize"]
+ toggles.buttonHeight = br.data.settings["buttonSize"]
+ if not toggles.mainButton then
+ toggles.mainButton = br._G.CreateFrame("Button", "MyButtonBR", br._G.UIParent, "SecureHandlerClickTemplate")
end
- local mainButtonFrame = br._G.CreateFrame("Frame", nil, br.mainButton)
- br.mainButton:SetWidth(br.buttonWidth)
- br.mainButton:SetHeight(br.buttonHeight)
- br.mainButton:RegisterForClicks("AnyUp")
+ local mainButtonFrame = br._G.CreateFrame("Frame", nil, toggles.mainButton)
+ toggles.mainButton:SetWidth(toggles.buttonWidth)
+ toggles.mainButton:SetHeight(toggles.buttonHeight)
+ toggles.mainButton:RegisterForClicks("AnyUp")
local anchor, x, y
if not br.data.settings.mainButton then
anchor = "CENTER"
@@ -31,33 +33,33 @@ function br.TogglesFrame()
x = br.data.settings.mainButton.pos.x or -75
y = br.data.settings.mainButton.pos.y or -200
end
- br.mainButton:ClearAllPoints()
- br.mainButton:SetPoint(anchor, x, y)
- br.mainButton:EnableMouse(true)
- br.mainButton:SetMovable(true)
- br.mainButton:SetClampedToScreen(true)
- br.mainButton:RegisterForDrag("LeftButton")
- br.mainButton:SetScript("OnDragStart", br.mainButton.StartMoving)
- br.mainButton:SetScript("OnDragStop", br.mainButton.StopMovingOrSizing)
+ toggles.mainButton:ClearAllPoints()
+ toggles.mainButton:SetPoint(anchor, x, y)
+ toggles.mainButton:EnableMouse(true)
+ toggles.mainButton:SetMovable(true)
+ toggles.mainButton:SetClampedToScreen(true)
+ toggles.mainButton:RegisterForDrag("LeftButton")
+ toggles.mainButton:SetScript("OnDragStart", toggles.mainButton.StartMoving)
+ toggles.mainButton:SetScript("OnDragStop", toggles.mainButton.StopMovingOrSizing)
-- Set Main Button
if
- br.data.settings[br.selectedSpec] ~= nil and br.data.settings[br.selectedSpec].toggles ~= nil and
- br.data.settings[br.selectedSpec].toggles["Power"] == 1
+ br.data.settings[br.loader.selectedSpec] ~= nil and br.data.settings[br.loader.selectedSpec].toggles ~= nil and
+ br.data.settings[br.loader.selectedSpec].toggles["Power"] == 1
then
- br.mainButton:SetNormalTexture(br.backIconOn)
+ toggles.mainButton:SetNormalTexture(toggles.backIconOn)
else
- br.mainButton:SetNormalTexture(br.backIconOff)
+ toggles.mainButton:SetNormalTexture(toggles.backIconOff)
end
- br.mainButton:SetScript(
+ toggles.mainButton:SetScript(
"OnClick",
function()
- if br.data.settings[br.selectedSpec].toggles["Power"] ~= 0 then
- br.data.settings[br.selectedSpec].toggles["Power"] = 0
- br.mainButton:SetNormalTexture(br.backIconOff)
+ if br.data.settings[br.loader.selectedSpec].toggles["Power"] ~= 0 then
+ br.data.settings[br.loader.selectedSpec].toggles["Power"] = 0
+ toggles.mainButton:SetNormalTexture(toggles.backIconOff)
-- on/off switch
- if br.data.settings[br.selectedSpec]["Start/Stop BadRotationsCheck"] ~= 0 then
- br.data.settings[br.selectedSpec]["Start/Stop BadRotationsCheck"] = 0
- br.ChatOverlay("|cFFFF0000-= BadRotations Off =-")
+ if br.data.settings[br.loader.selectedSpec]["Start/Stop BadRotationsCheck"] ~= 0 then
+ br.data.settings[br.loader.selectedSpec]["Start/Stop BadRotationsCheck"] = 0
+ br.ui.chatOverlay:Show("|cFFFF0000-= BadRotations Off =-")
end
br._G.GameTooltip:SetText(
"|cff00FF00Enable |cffFF0000BadRotations \n|cffFFDD11Hold Left Alt and scroll mouse to adjust size.",
@@ -65,13 +67,14 @@ function br.TogglesFrame()
225 / 255,
225 / 255
)
- mainButtonFrame.texture:SetTexture(br.genericIconOff)
+ mainButtonFrame.texture:SetTexture(toggles.genericIconOff)
+
else
- br.data.settings[br.selectedSpec].toggles["Power"] = 1
+ br.data.settings[br.loader.selectedSpec].toggles["Power"] = 1
-- on/off switch
- if br.data.settings[br.selectedSpec]["Start/Stop BadRotationsCheck"] ~= 1 then
- br.data.settings[br.selectedSpec]["Start/Stop BadRotationsCheck"] = 1
- br.ChatOverlay("|cFF00FF00-= BadRotations On =-")
+ if br.data.settings[br.loader.selectedSpec]["Start/Stop BadRotationsCheck"] ~= 1 then
+ br.data.settings[br.loader.selectedSpec]["Start/Stop BadRotationsCheck"] = 1
+ br.ui.chatOverlay:Show("|cFF00FF00-= BadRotations On =-")
end
br._G.GameTooltip:SetText(
"|cffFF0000Disable BadRotations \n|cffFFDD11Hold Left Alt and scroll mouse to adjust size.",
@@ -79,16 +82,17 @@ function br.TogglesFrame()
225 / 255,
225 / 255
)
- br.mainButton:SetNormalTexture(br.backIconOn)
- mainButtonFrame.texture:SetTexture(br.genericIconOn)
+ toggles.mainButton:SetNormalTexture(toggles.backIconOn)
+ mainButtonFrame.texture:SetTexture(toggles.genericIconOn)
+
end
end
)
- br.mainButton:SetScript(
+ toggles.mainButton:SetScript(
"OnEnter",
function()
- br._G.GameTooltip:SetOwner(br.mainButton, 0, 0)
- if br.data.settings[br.selectedSpec].toggles["Power"] == 1 then
+ br._G.GameTooltip:SetOwner(toggles.mainButton, 0, 0)
+ if br.data.settings[br.loader.selectedSpec].toggles["Power"] == 1 then
br._G.GameTooltip:SetText(
"|cffFF0000Disable BadRotations \n|cffFFDD11Hold Left Alt and scroll mouse to adjust size.",
225 / 255,
@@ -106,13 +110,13 @@ function br.TogglesFrame()
br._G.GameTooltip:Show()
end
)
- br.mainButton:SetScript(
+ toggles.mainButton:SetScript(
"OnLeave",
function()
br._G.GameTooltip:Hide()
end
)
- -- br.mainButton:SetScript(
+ -- toggles.mainButton:SetScript(
-- "OnReceiveDrag",
-- function(frame)
-- local self = frame.obj
@@ -123,17 +127,19 @@ function br.TogglesFrame()
-- br.data.settings.mainButton.pos.anchor = anchor
-- end
-- )
- br.mainButton:SetScript(
+ toggles.mainButton:SetScript(
"OnDragStop",
function()
- local _, _, anchor, x, y = br.mainButton:GetPoint(1)
+ local _, _, anchor, x, y = toggles.mainButton:GetPoint(1)
br.data.settings.mainButton.pos.x = x
br.data.settings.mainButton.pos.y = y
br.data.settings.mainButton.pos.anchor = anchor
- br.mainButton:StopMovingOrSizing()
+ -- Persist updated position immediately
+ br.ui.settingsManagement:saveSettings(nil, nil, br.loader.selectedSpec, br.loader.selectedProfileName)
+ toggles.mainButton:StopMovingOrSizing()
end
)
- br.mainButton:SetScript(
+ toggles.mainButton:SetScript(
"OnMouseWheel",
function(_, delta)
if br._G.IsLeftAltKeyDown() then
@@ -145,15 +151,15 @@ function br.TogglesFrame()
end
if Go == true then
br.data.settings["buttonSize"] = br.data.settings["buttonSize"] + delta
- br.mainButton:SetWidth(br.data.settings["buttonSize"])
- br.mainButton:SetHeight(br.data.settings["buttonSize"])
- br.mainText:SetTextHeight(br.data.settings["buttonSize"] / 3)
- br.mainText:SetPoint("CENTER", 0, -(br.data.settings["buttonSize"] / 4))
+ toggles.mainButton:SetWidth(br.data.settings["buttonSize"])
+ toggles.mainButton:SetHeight(br.data.settings["buttonSize"])
+ toggles.mainText:SetTextHeight(br.data.settings["buttonSize"] / 3)
+ toggles.mainText:SetPoint("CENTER", 0, -(br.data.settings["buttonSize"] / 4))
mainButtonFrame:SetWidth(br.data.settings["buttonSize"] * 1.67)
mainButtonFrame:SetHeight(br.data.settings["buttonSize"] * 1.67)
mainButtonFrame.texture:SetWidth(br.data.settings["buttonSize"] * 1.67)
mainButtonFrame.texture:SetHeight(br.data.settings["buttonSize"] * 1.67)
- br.buttonsResize()
+ br.ui:buttonsResize()
end
end
end
@@ -166,25 +172,25 @@ function br.TogglesFrame()
mainButtonFrame.texture:SetWidth(br.data.settings["buttonSize"] * 1.67)
mainButtonFrame.texture:SetHeight(br.data.settings["buttonSize"] * 1.67)
mainButtonFrame.texture:SetAlpha(1)
- mainButtonFrame.texture:SetTexture(br.genericIconOn)
- br.mainText = br.mainButton:CreateFontString(nil, "OVERLAY")
- br.mainText:SetFont(br.data.settings.font, br.data.settings.fontsize, "THICKOUTLINE")
- br.mainText:SetTextHeight(br.data.settings["buttonSize"] / 3)
- br.mainText:SetPoint("CENTER", 3, -(br.data.settings["buttonSize"] / 8))
- br.mainText:SetTextColor(.90, .90, .90, 1)
- if br.data.settings[br.selectedSpec] ~= nil then
- if br.data.settings[br.selectedSpec].toggles == nil then
- br.data.settings[br.selectedSpec].toggles = {}
+ mainButtonFrame.texture:SetTexture(toggles.genericIconOn)
+ toggles.mainText = toggles.mainButton:CreateFontString(nil, "OVERLAY")
+ toggles.mainText:SetFont(br.data.settings.font, br.data.settings.fontsize, "THICKOUTLINE")
+ toggles.mainText:SetTextHeight(br.data.settings["buttonSize"] / 3)
+ toggles.mainText:SetPoint("CENTER", 3, -(br.data.settings["buttonSize"] / 8))
+ toggles.mainText:SetTextColor(.90, .90, .90, 1)
+ if br.data.settings[br.loader.selectedSpec] ~= nil then
+ if br.data.settings[br.loader.selectedSpec].toggles == nil then
+ br.data.settings[br.loader.selectedSpec].toggles = {}
end
- if br.data.settings[br.selectedSpec].toggles["Power"] == 0 then
- br.data.settings[br.selectedSpec].toggles["Power"] = 0
- br.mainText:SetText("Off")
- mainButtonFrame.texture:SetTexture(br.genericIconOff)
+ if br.data.settings[br.loader.selectedSpec].toggles["Power"] == 0 then
+ br.data.settings[br.loader.selectedSpec].toggles["Power"] = 0
+ toggles.mainText:SetText("Off")
+ mainButtonFrame.texture:SetTexture(toggles.genericIconOff)
else
- br.data.settings[br.selectedSpec].toggles["Power"] = 1
- br.mainText:SetText("On")
- mainButtonFrame.texture:SetTexture(br.genericIconOn)
+ br.data.settings[br.loader.selectedSpec].toggles["Power"] = 1
+ toggles.mainText:SetText("On")
+ mainButtonFrame.texture:SetTexture(toggles.genericIconOn)
end
end
- br.buttonsTable = {}
+ toggles.buttonsTable = {}
end
diff --git a/System/UI/Tooltips.lua b/System/UI/Tooltips.lua
new file mode 100644
index 0000000000..97ac25974e
--- /dev/null
+++ b/System/UI/Tooltips.lua
@@ -0,0 +1,416 @@
+local _, br = ...
+br.ui.tooltips = br.ui.tooltips or {}
+local tooltips = br.ui.tooltips
+
+------------------------------------------------------------------------------------------------------------------------
+-- idTip by Silverwind
+local hooksecurefunc, select, UnitBuff, UnitDebuff, UnitAura, UnitGUID, _, tonumber, strfind =
+ br._G.hooksecurefunc,
+ br._G.select,
+ br._G.UnitBuff,
+ br._G.UnitDebuff,
+ br._G.UnitAura,
+ br._G.UnitGUID,
+ br._G.GetGlyphSocketInfo,
+ br._G.tonumber,
+ br._G.strfind
+local types = {
+ spell = "SpellID:",
+ item = "ItemID:",
+ glyph = "GlyphID:",
+ unit = "NPC ID:",
+ quest = "QuestID:",
+ talent = "TalentID:",
+ achievement = "AchievementID:"
+}
+local function addLine(tooltip, id, type, noEmptyLine)
+ local found = false
+ -- Check if we already added to this tooltip. Happens on the talent frame
+ for i = 1, 15 do
+ local frame = _G[tooltip:GetName() .. "TextLeft" .. i]
+ local text
+ if frame then
+ text = frame:GetText()
+ end
+ if text and text == type then
+ found = true
+ break
+ end
+ end
+ if not found then
+ if not noEmptyLine then
+ tooltip:AddLine(" ")
+ end
+ tooltip:AddDoubleLine(type, "|cffffffff" .. id)
+ tooltip:Show()
+ end
+end
+-- All types, primarily for linked tooltips
+local function onSetHyperlink(self, link)
+ local type, id = string.match(link, "^(%a+):(%d+)")
+ if not type or not id then
+ return
+ end
+ if type == "spell" or type == "enchant" or type == "trade" then
+ addLine(self, id, types.spell)
+ elseif type == "glyph" then
+ addLine(self, id, types.glyph)
+ elseif type == "talent" then
+ addLine(self, id, types.talent)
+ elseif type == "quest" then
+ addLine(self, id, types.quest)
+ elseif type == "achievement" then
+ addLine(self, id, types.achievement)
+ elseif type == "item" then
+ addLine(self, id, types.item)
+ end
+end
+hooksecurefunc(ItemRefTooltip, "SetHyperlink", onSetHyperlink)
+hooksecurefunc(GameTooltip, "SetHyperlink", onSetHyperlink)
+-- Spells
+hooksecurefunc(
+ br._G.GameTooltip,
+ "SetUnitBuff",
+ function(self, ...)
+ local id = select(10, br._G.UnitBuff(...))
+ if id then
+ addLine(self, id, types.spell)
+ end
+ end
+)
+hooksecurefunc(
+ br._G.GameTooltip,
+ "SetUnitDebuff",
+ function(self, ...)
+ local id = select(10, br._G.UnitDebuff(...))
+ if id then
+ addLine(self, id, types.spell)
+ end
+ end
+)
+hooksecurefunc(
+ br._G.GameTooltip,
+ "SetUnitAura",
+ function(self, ...)
+ local unitAura = C_UnitAuras.GetAuraDataByIndex(...) --UnitAura(...)
+ local id = unitAura and unitAura.spellId or nil
+ if id then
+ addLine(self, id, types.spell)
+ end
+ end
+)
+hooksecurefunc(
+ "SetItemRef",
+ function(link, ...)
+ local id = tonumber(link:match("spell:(%d+)"))
+ if id then
+ addLine(br._G.ItemRefTooltip, id, types.spell)
+ end
+ end
+)
+-- Spell tooltips (uses compatibility wrapper for Retail vs Classic)
+if br.api and br.api.wow and br.api.wow.HookTooltipSetSpell then
+ br.api.wow.HookTooltipSetSpell(function(tooltip, id)
+ addLine(tooltip, id, types.spell)
+ end)
+end
+-- NPCs
+local function unitTooltipHandler(tooltip)
+ if br._G.C_PetBattles and br._G.C_PetBattles.IsInBattle and br._G.C_PetBattles.IsInBattle() then
+ return
+ end
+
+ local unit = select(2, tooltip:GetUnit())
+ if br.functions.misc:isChecked("Unit ID In Tooltip") and unit then
+ local guid = UnitGUID(unit) or ""
+ local id = tonumber(guid:match("-(%d+)-%x+$"), 10)
+ local type = guid:match("%a+")
+ -- ID 970 seems to be used for players
+ if id and type ~= "Player" then
+ addLine(tooltip, id, types.unit)
+ end
+ end
+end
+
+-- Target Validation Debug Tooltip Handler
+function tooltips:targetValidationDebugHandler(tooltip)
+ -- Check if Target Validation Debug is enabled first
+ if not br.functions.misc:isChecked("Target Validation Debug") then
+ return
+ end
+
+ -- Skip during pet battles
+ if br._G.C_PetBattles and br._G.C_PetBattles.IsInBattle and br._G.C_PetBattles.IsInBattle() then
+ return
+ end
+
+ -- Get the unit from tooltip
+ local unit = select(2, tooltip:GetUnit())
+ if not unit then
+ return
+ end
+
+ -- Get GUID using standard WoW API
+ if not br._G.UnitGUID then
+ return
+ end
+
+ local unitGUID = br._G.UnitGUID(unit)
+ if not unitGUID then
+ return
+ end
+
+ -- Only show debug for non-player units (or charmed/mind controlled players)
+ local guidType = unitGUID:match("%a+")
+ if guidType == "Player" and not br._G.UnitIsCharmed(unit) then
+ return
+ end
+
+ -- Check if we have unlocker for advanced validation checks
+ if not (br.unlocked and br._G.GetObjectCount ~= nil) then
+ return
+ end
+
+ -- Gather all validation data
+ local distance = br.functions.range:getDistance(unit, "player")
+ local isDummy = br.functions.unit:isDummy(unit)
+ local dummy = false
+ if isDummy then
+ if br.functions.unit:GetUnitIsUnit(unit, "target") then
+ dummy = true
+ else
+ if br.functions.unit:GetObjectExists("target") and br.functions.unit:isDummy("target") then
+ local distToTarget = br.functions.range:getDistance(unit, "target")
+ if distToTarget ~= nil and distToTarget <= 8 then
+ dummy = true
+ end
+ end
+ end
+ end
+ local burnUnit = br.functions.misc:getOptionCheck("Forced Burn") and br.engines.enemiesEngineFunctions:isBurnTarget(unitGUID) > 0
+ local playerTarget = br.functions.unit:GetUnitIsUnit(unit, "target")
+ local targeting = br.functions.misc:isTargeting(unitGUID)
+ local hasThreat = br.functions.combat:hasThreat(unitGUID)
+ local reaction = br.functions.unit:GetUnitReaction(unit, "player") or 10
+ local isCC = br.functions.misc:getOptionCheck("Don't break CCs") and br.functions.misc:isLongTimeCCed(unitGUID)
+ local isTapped = br._G.UnitIsTapDenied(unit)
+ local hasDamagedTapped = isTapped and br.engines.enemiesEngine.damaged[br._G.ObjectPointer(unit)] ~= nil
+ local inUnitsTable = br.engines.enemiesEngine.units[unitGUID] ~= nil
+ local isFriend = br.functions.unit:GetUnitIsFriend(unit, "player")
+ local isCharmed = br._G.UnitIsCharmed(unit)
+ local canAttack = br._G.UnitCanAttack("player", unit)
+ local isSafe = br.engines.enemiesEngineFunctions:isSafeToAttack(unitGUID)
+ local hostileOnly = br.functions.misc:isChecked("Hostiles Only")
+ local attackMC = br.functions.misc:isChecked("Attack MC Targets")
+ local inPhase = br._G.UnitInPhase(unit)
+ local los = br.functions.misc:getLineOfSight("player", unit)
+ local health = br._G.UnitHealth(unit)
+ local isDead = br.functions.unit:GetUnitIsDeadOrGhost(unit)
+ local isCritter = br.functions.unit:isCritter(unit)
+ local isPet = br.functions.unit:GetUnitIsUnit(unit, "pet")
+
+ -- Check all conditions and only show failures
+ if isDead then
+ tooltip:AddLine("Dead or Ghost", 1, 0, 0)
+ return
+ end
+
+ -- Phase Sync Check (debuff 310499 - Shadowlands phasing, not used in Classic)
+ local targetHasPhaseDebuff = br.functions.aura:UnitDebuffID(unit, 310499)
+ local playerHasPhaseDebuff = br.functions.aura:UnitDebuffID("player", 310499)
+ if (targetHasPhaseDebuff and not playerHasPhaseDebuff) or (not targetHasPhaseDebuff and playerHasPhaseDebuff) then
+ tooltip:AddLine("Phase Sync Failed", 1, 0, 0)
+ return
+ end
+
+ if not inPhase then
+ tooltip:AddLine("Not in Phase", 1, 0, 0)
+ return
+ end
+
+ if not canAttack then
+ tooltip:AddLine("Cannot Attack", 1, 0, 0)
+ return
+ end
+
+ if health <= 0 then
+ tooltip:AddLine("No Health", 1, 0, 0)
+ return
+ end
+
+ if distance >= 50 then
+ tooltip:AddLine("Too Far (" .. string.format("%.1f", distance) .. "yd)", 1, 0, 0)
+ return
+ end
+
+ if isCritter then
+ tooltip:AddLine("Is Critter", 1, 0, 0)
+ return
+ end
+
+ if isPet then
+ tooltip:AddLine("Is Your Pet", 1, 0, 0)
+ return
+ end
+
+ -- Check if created by player (but allow hostile quest NPCs)
+ local createdByPlayer = br._G.UnitCreator(unit) == br._G.ObjectPointer("player")
+ local unitReaction = br._G.UnitReaction(unit, "player") or 5
+ local isHostileQuestNPC = createdByPlayer and unitReaction < 4
+ if createdByPlayer and not isHostileQuestNPC then
+ tooltip:AddLine("Created/Summoned by You", 1, 0, 0)
+ return
+ end
+
+ -- Check if water elemental
+ local objectID = br.functions.unit:GetObjectID(unit)
+ if objectID == 11492 then
+ tooltip:AddLine("Is Water Elemental (11492)", 1, 0, 0)
+ return
+ end
+
+ -- MC Check
+ local mcCheck = (attackMC and (not isFriend or (isCharmed and canAttack))) or not isFriend
+ if not mcCheck then
+ if isFriend and not isCharmed then
+ tooltip:AddLine("Is Friendly (not charmed)", 1, 0, 0)
+ elseif isFriend and not attackMC then
+ tooltip:AddLine("Is Charmed (MC targeting disabled)", 1, 0, 0)
+ end
+ return
+ end
+
+ if not los then
+ tooltip:AddLine("No Line of Sight", 1, 0, 0)
+ return
+ end
+
+ -- Units table or special cases (now includes threat check)
+ if not (inUnitsTable or playerTarget or burnUnit or dummy or hasThreat) then
+ tooltip:AddLine("Not in Units Table", 1, 0, 0)
+ tooltip:AddLine(" (Not your target/burn/dummy/threat)", 1, 0.5, 0)
+ return
+ end
+
+ -- CC Check
+ if isCC then
+ tooltip:AddLine("Is CC'd (Don't break CCs enabled)", 1, 0, 0)
+ return
+ end
+
+ -- Tap Check
+ if isTapped and not (dummy or burnUnit or hasDamagedTapped) then
+ tooltip:AddLine("Tap Denied (Haven't damaged it)", 1, 0, 0)
+ return
+ end
+
+ -- Safe to Attack Check
+ if not (dummy or burnUnit or isSafe) then
+ tooltip:AddLine("Not Safe to Attack", 1, 0, 0)
+ tooltip:AddLine(" (CC candidate/No-touch unit)", 1, 0.5, 0)
+ return
+ end
+
+ -- Reaction Check
+ local reactionPass = (hostileOnly and reaction < 4) or (not hostileOnly and reaction < 5) or playerTarget or targeting
+ if not reactionPass and not (dummy or burnUnit) then
+ if hostileOnly then
+ tooltip:AddLine("Reaction " .. reaction .. " (need <4 for Hostiles Only)", 1, 0, 0)
+ else
+ tooltip:AddLine("Reaction " .. reaction .. " (need <5)", 1, 0, 0)
+ end
+ return
+ end
+
+ -- Final validation check
+ local inInstance = br._G.IsInInstance()
+ local friendCount = #br.engines.healingEngine.friend
+ local hasTankResult = br.functions.misc:hasTank()
+ local instanceCheckForPlayerTarget = (not inInstance) or (inInstance and (friendCount == 1 or not hasTankResult))
+ local provingGround = br.functions.misc:isInProvingGround()
+
+ if br.functions.misc:isValidUnit(unitGUID) then
+ tooltip:AddLine("Unit is Valid", 0, 1, 0)
+ else
+ -- Show why threat check failed
+ if playerTarget and not instanceCheckForPlayerTarget then
+ tooltip:AddLine("Not solo/no tank in instance", 1, 0, 0)
+ elseif not playerTarget and not targeting and not burnUnit and not provingGround and not hasThreat then
+ tooltip:AddLine("No threat conditions met", 1, 0, 0)
+ else
+ tooltip:AddLine("Failed validation", 1, 0, 0)
+ end
+ end
+end
+
+-- Register tooltip hook (uses compatibility wrapper for Retail vs Classic)
+if br.api and br.api.wow and br.api.wow.HookTooltipSetUnit then
+ br.api.wow.HookTooltipSetUnit(unitTooltipHandler)
+ br.api.wow.HookTooltipSetUnit(function(tooltip) tooltips:targetValidationDebugHandler(tooltip) end)
+end
+-- Items
+local function attachItemTooltip(self)
+ if (self == GameTooltip or self == ItemRefTooltip) then
+ local link = select(2, self:GetItem())
+ if link then
+ local id = select(3, strfind(link, "^|%x+|Hitem:(%-?%d+):(%d+):(%d+).*"))
+ if id then
+ addLine(self, id, types.item)
+ end
+ end
+ end
+end
+
+-- Register item tooltip hooks (uses compatibility wrapper for Retail vs Classic)
+if br.api and br.api.wow and br.api.wow.HookTooltipSetItem then
+ br.api.wow.HookTooltipSetItem(br._G.GameTooltip, attachItemTooltip)
+ br.api.wow.HookTooltipSetItem(br._G.ItemRefTooltip, attachItemTooltip)
+end
+-- Glyphs -- Commented out due to Legion
+-- hooksecurefunc(GameTooltip, "SetGlyph", function(self, ...)
+-- local id = select(4, GetGlyphSocketInfo(...))
+-- if id then addLine(self, id, types.glyph) end
+-- end)
+-- hooksecurefunc(GameTooltip, "SetGlyphByID", function(self, id)
+-- if id then addLine(self, id, types.glyph) end
+-- end)
+-- Achievement Frame Tooltips
+-- local f = br._G.CreateFrame("frame")
+-- f:RegisterEvent("ADDON_LOADED")
+-- f:SetScript(
+-- "OnEvent",
+-- function(_, _, what)
+-- if what == "Blizzard_AchievementUI" then
+-- for _, button in ipairs(br._G.AchievementFrameAchievements.ScrollBox.ScrollTarget:GetChildren()) do
+-- button:HookScript(
+-- "OnEnter",
+-- function()
+-- br._G.GameTooltip:SetOwner(button, "ANCHOR_NONE")
+-- br._G.GameTooltip:SetPoint("TOPLEFT", button, "TOPRIGHT", 0, 0)
+-- addLine(br._G.GameTooltip, button.id, types.achievement, true)
+-- br._G.GameTooltip:Show()
+-- end
+-- )
+-- button:HookScript(
+-- "OnLeave",
+-- function()
+-- br._G.GameTooltip:Hide()
+-- end
+-- )
+-- end
+-- end
+-- end
+-- )
+-- local petAbilityTooltipID = false
+-- local orig_SharedPetBattleAbilityTooltip_SetAbility = br._G.SharedPetBattleAbilityTooltip_SetAbility
+-- function br.SharedPetBattleAbilityTooltip_SetAbility(self, abilityInfo, additionalText)
+-- orig_SharedPetBattleAbilityTooltip_SetAbility(self, abilityInfo, additionalText)
+-- petAbilityTooltipID = abilityInfo:GetAbilityID()
+-- end
+-- br._G.PetBattlePrimaryAbilityTooltip:HookScript(
+-- "OnShow",
+-- function(self)
+-- local name = self.Name:GetText()
+-- self.Name:SetText(name .. " (ID: " .. petAbilityTooltipID .. ")")
+-- end
+-- )
\ No newline at end of file
diff --git a/System/UI/Windows/Config.lua b/System/UI/Windows/Config.lua
index b0f44a8097..c618b698ac 100644
--- a/System/UI/Windows/Config.lua
+++ b/System/UI/Windows/Config.lua
@@ -2,8 +2,14 @@ local _, br = ...
-- This creates the normal BadRotations Configuration Window
br.ui.window.config = {}
function br.ui:createConfigWindow()
+ -- br._G.print("Creating Config Window") -- Debug
br.ui.window.config = br.ui:createWindow("config", 275, 400, "Configuration")
+ if not br.ui.window.config then
+ br._G.print("Error: Failed to create config window")
+ return
+ end
+
local section
local function callGeneral()
-- General
@@ -18,12 +24,6 @@ function br.ui:createConfigWindow()
0.01,
"Adjust the update rate of Bot operations. Increase to improve FPS but may cause reaction delays. Will be ignored if Auto Delay is checked. Default: 0.1"
)
- br.rotationLog = br.ui:createCheckbox(section, "Rotation Log", "Display Rotation Log.")
- br.ui:createDropdown(section, "Addon Debug Messages", { "System Only", "Profile Only", "All" }, 3,
- "Check this to display developer debug messages.")
- br.targetval = br.ui:createCheckbox(section, "Target Validation Debug",
- "Check this to display current target's validation.")
- br.ui:createCheckbox(section, "Display Failcasts", "Dispaly Failcasts in Debug.")
br.ui:createCheckbox(section, "Queue Casting", "Allow Queue Casting on some profiles.")
br.ui:createSpinner(section, "Auto Loot", 0.5, 0.1, 3, 0.1, "Sets Autloot on/off.", "Sets a delay for Auto Loot.")
br.ui:createCheckbox(section, "Auto-Sell/Repair",
@@ -38,8 +38,6 @@ function br.ui:createConfigWindow()
"|cffFF0000 WARNING!|cffFFFFFF Checking this will reset saved profiles on reload!")
br.ui:createCheckbox(section, "Auto Check for Updates",
"EWT only. This uses the Git master head sha for comparison. |cffFF0000Experimental!")
- br.ui:createCheckbox(section, "Dev Mode",
- "|cffFF0000WARNING! This will expose the BR table to global for testing purposes. Do not use unless you know what you're doing.")
br.ui:checkSectionState(section)
end
@@ -196,10 +194,6 @@ function br.ui:createConfigWindow()
1,
"Using the bait."
)
- br.ui:createCheckbox(section, "Debug Timers", "Useless to users, for Devs.")
- br.ui:createCheckbox(section, "Cache Debuffs", "Experimental feature still in testing")
- br.ui:createCheckbox(section, "Unit ID In Tooltip", "Show/Hide Unit IDs in Tooltip")
- br.ui:createCheckbox(section, "Show Drawings", "Show drawings on screen using Lib Draw")
br.ui:checkSectionState(section)
section = br.ui:createSection(br.ui.window.config, "Dungeon Helpers")
br.ui:createCheckbox(section, "Quaking Helper",
@@ -217,24 +211,24 @@ function br.ui:createConfigWindow()
"Select profile to use, then click load")
br.ui:createText(section, "|cffDB4437Save your settings before loading a new one!!")
local saveProfile = function()
- br:saveSettings("Profile Settings", br.player.class, br.selectedSpec, br.selectedProfileName,
- profileSettings[br.getValue("Select Settings")])
+ br.ui.settingsManagement:saveSettings("Profile Settings", br.player.class, br.loader.selectedSpec, br.loader.selectedProfileName,
+ profileSettings[br.functions.misc:getValue("Select Settings")])
end
local loadProfile = function()
br.data.loadedSettings = false
local loadDir =
- br:checkDirectories("Profile Settings", br.player.class, br.selectedSpec, br.selectedProfileName,
- profileSettings[br.getValue("Select Settings")])
+ br.ui.settingsManagement:checkDirectories("Profile Settings", br.player.class, br.loader.selectedSpec, br.loader.selectedProfileName,
+ profileSettings[br.functions.misc:getValue("Select Settings")])
if not loadDir then
br._G.print("Load Directory is nil!")
return
end
- if loadDir and br:findFileInFolder("savedSettings.lua", loadDir) then
- br:loadSettings("Profile Settings", br.player.class, br.selectedSpec, br.selectedProfileName,
- profileSettings[br.getValue("Select Settings")])
+ if loadDir and br.ui.settingsManagement:findFileInFolder("savedSettings.lua", loadDir) then
+ br.ui.settingsManagement:loadSettings("Profile Settings", br.player.class, br.loader.selectedSpec, br.loader.selectedProfileName,
+ profileSettings[br.functions.misc:getValue("Select Settings")])
br.rotationChanged = true
else
- br._G.print("You don't have saved setting for :" .. profileSettings[br.getValue("Select Settings")])
+ br._G.print("You don't have saved setting for :" .. profileSettings[br.functions.misc:getValue("Select Settings")])
end
end
local y = -5
@@ -243,7 +237,7 @@ function br.ui:createConfigWindow()
y = y - section.children[i].frame:GetHeight() * 1.2
end
end
- y = br.round2(y, 1)
+ y = br.functions.misc:round2(y, 1)
br.ui:createButton(section, "Save", 10, y, saveProfile)
br.ui:createButton(section, "Load", -10, y, loadProfile, true)
br.ui:checkSectionState(section)
@@ -253,7 +247,7 @@ function br.ui:createConfigWindow()
-- -90)
br.ui:createImportButton(section, "Import", 140, y)
-- -90)
- br.ui:createText(section, "FileName: " .. br.selectedSpec .. br.selectedProfileName .. ".lua")
+ br.ui:createText(section, "FileName: " .. br.loader.selectedSpec .. br.loader.selectedProfileName .. ".lua")
br.ui:checkSectionState(section)
end
@@ -271,6 +265,29 @@ function br.ui:createConfigWindow()
br.ui:checkSectionState(section)
end
+ local function callDebugOptions()
+ -- Debug and Developer Options
+ section = br.ui:createSection(br.ui.window.config, "Debug Options")
+ br.ui.rotationLog = br.ui:createCheckbox(section, "Rotation Log", "Display Rotation Log.")
+ br.ui:createDropdown(section, "Addon Debug Messages", { "System Only", "Profile Only", "All" }, 3,
+ "Check this to display developer debug messages.")
+ br.ui:createCheckbox(section, "Target Validation Debug",
+ "Check this to display current target's validation.")
+ br.ui:createCheckbox(section, "Display Failcasts", "Dispaly Failcasts in Debug.")
+ br.ui:createCheckbox(section, "Cast Debug", "Shows information about how the bot is casting.")
+ br.ui:createCheckbox(section, "Threat Debug", "Shows information about threat and threat changes.")
+ br.ui:checkSectionState(section)
+
+ section = br.ui:createSection(br.ui.window.config, "Developer Options")
+ br.ui:createCheckbox(section, "Dev Mode",
+ "|cffFF0000WARNING! This will expose the BR table to global for testing purposes. Do not use unless you know what you're doing.")
+ br.ui:createCheckbox(section, "Debug Timers", "Useless to users, for Devs.")
+ br.ui:createCheckbox(section, "Cache Debuffs", "Experimental feature still in testing")
+ br.ui:createCheckbox(section, "Unit ID In Tooltip", "Show/Hide Unit IDs in Tooltip")
+ br.ui:createCheckbox(section, "Show Drawings", "Show drawings on screen using Lib Draw")
+ br.ui:checkSectionState(section)
+ end
+
local function callQueueEngine()
local function pairsByKeys(t, f)
local a = {}
@@ -295,14 +312,15 @@ function br.ui:createConfigWindow()
"Seconds to attempt cast")
if br.player ~= nil and br.player.spells ~= nil and br.player.spells.abilities ~= nil then
for _, v in pairsByKeys(br.player.spells.abilities) do
- local spellName = br._G.GetSpellInfo(v)
- if v ~= 61304 and spellName ~= nil then
+ local id = type(v) == "table" and (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(v) or v[1]) or v
+ local spellName = br.api.wow.GetSpellInfo(id)
+ if id ~= 61304 and spellName ~= nil then
br.ui:createDropdown(
section,
spellName .. " (Queue)",
{ "Normal", "Cursor", "Cursor (No Cast)", "Mouseover" },
1,
- "Active Queueing Of " .. spellName .. " (ID: " .. v .. ")",
+ "Active Queueing Of " .. spellName .. " (ID: " .. id .. ")",
"Select cast mode"
)
end
@@ -380,6 +398,10 @@ function br.ui:createConfigWindow()
[1] = "Tracker Engine",
[2] = callTrackerEngine
},
+ {
+ [1] = "Debug",
+ [2] = callDebugOptions
+ },
-- {
-- [1] = "Debug Info",
-- [2] = callDebugOptions
diff --git a/System/UI/Windows/Debug.lua b/System/UI/Windows/Debug.lua
index 5e223a7a13..bcee2ec4d0 100644
--- a/System/UI/Windows/Debug.lua
+++ b/System/UI/Windows/Debug.lua
@@ -7,18 +7,18 @@ function br.ui:createDebugWindow()
br.ui.window.debug.parent.closeButton:SetScript(
"OnClick",
function()
- if br.data.settings[br.selectedSpec][br.selectedProfile] ~= nil and br.data.settings[br.selectedSpec][br.selectedProfile]["General"] ~= nil then
- br.data.settings[br.selectedSpec][br.selectedProfile]["General"]["Rotation Log Check"] = false
- if br.data.ui["General"] == nil then br.data.ui["General"] = {} end
- br.data.ui["General"]["Rotation Log Check"] = false
+ if br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile] ~= nil and br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["Debug"] ~= nil then
+ br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["Debug"]["Rotation Log Check"] = false
+ if br.data.ui["Debug"] == nil then br.data.ui["Debug"] = {} end
+ br.data.ui["Debug"]["Rotation Log Check"] = false
end
- if br.rotationLog ~= nil then
- br.rotationLog:SetChecked(false)
+ if br.ui.rotationLog ~= nil then
+ br.ui.rotationLog:SetChecked(false)
end
- if br.data.settings[br.selectedSpec].debug == nil then
- br.data.settings[br.selectedSpec].debug = {}
+ if br.data.settings[br.loader.selectedSpec].debug == nil then
+ br.data.settings[br.loader.selectedSpec].debug = {}
end
- br.data.settings[br.selectedSpec].debug["active"] = false
+ br.data.settings[br.loader.selectedSpec].debug["active"] = false
br.ui.window.debug.parent:Hide()
end
)
@@ -30,15 +30,15 @@ function br.ui:toggleDebugWindow()
br.ui:createDebugWindow()
br.ui:closeWindow("debug")
end
- if br.getOptionCheck("Rotation Log") then
+ if br.functions.misc:getOptionCheck("Rotation Log") then
if not br.ui.window["debug"]["parent"] then
br.ui:createDebugWindow()
end
br.ui:showWindow("debug")
- elseif br.data.settings[br.selectedSpec]["debug"] == nil then
- br.data.settings[br.selectedSpec]["debug"] = {}
- br.data.settings[br.selectedSpec]["debug"].active = false
- elseif br.data.settings[br.selectedSpec]["debug"].active == true then
+ elseif br.data.settings[br.loader.selectedSpec]["debug"] == nil then
+ br.data.settings[br.loader.selectedSpec]["debug"] = {}
+ br.data.settings[br.loader.selectedSpec]["debug"].active = false
+ elseif br.data.settings[br.loader.selectedSpec]["debug"].active == true then
br.ui:closeWindow("debug")
end
end
diff --git a/System/UI/Windows/Healing.lua b/System/UI/Windows/Healing.lua
index 18e4168d91..6873c2002f 100644
--- a/System/UI/Windows/Healing.lua
+++ b/System/UI/Windows/Healing.lua
@@ -6,13 +6,13 @@ function br.ui:createHealingWindow()
br.ui.window.healing.parent.closeButton:SetScript(
"OnClick",
function()
- if br.data.settings[br.selectedSpec][br.selectedProfile] ~= nil then
- br.data.settings[br.selectedSpec][br.selectedProfile]["Healing DebugCheck"] = false
+ if br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile] ~= nil then
+ br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["Healing DebugCheck"] = false
end
if br.healingDebug ~= nil then
br.healingDebug:SetChecked(false)
end
- br.data.settings[br.selectedSpec].healing["active"] = false
+ br.data.settings[br.loader.selectedSpec].healing["active"] = false
br.ui.window.healing.parent:Hide()
end
)
diff --git a/System/UI/Windows/Profile.lua b/System/UI/Windows/Profile.lua
index bc70f2f516..b338474bc6 100644
--- a/System/UI/Windows/Profile.lua
+++ b/System/UI/Windows/Profile.lua
@@ -1,7 +1,7 @@
local _, br = ...
br.ui.window.profile = {}
function br.ui:createProfileWindow(spec)
- if spec == "Initial" then
+ if spec == "Initial" or (br.isClassic or br.isBC) then
spec = br._G.UnitClass("player")
end
br.ui.window.profile = br.ui:createWindow("profile", 275, 400, spec)
diff --git a/System/Unlockers.lua b/System/Unlockers.lua
index 3a9db486fd..fb9335caba 100644
--- a/System/Unlockers.lua
+++ b/System/Unlockers.lua
@@ -1,26 +1,43 @@
local _, br = ...
local b = br._G
+br.unlockers = br.unlockers or {}
+local unlockers = br.unlockers
-function br:loadUnlockerAPI()
+br.unlockers.selected = "None"
+br.unlockers.unlock = {}
+
+function unlockers:loadUnlockerAPI()
local unlocked = false
--local class = self.class
- if self.unlock["NNUnlock"] ~= nil and self.unlock:NNUnlock() then
+ if unlockers["NNUnlock"] ~= nil and unlockers:NNUnlock() then
unlocked = true
- elseif self.unlock["TinkrUnlock"] ~= nil and self.unlock.TinkrUnlock() then
+ elseif unlockers["TinkrUnlock"] ~= nil and unlockers:TinkrUnlock() then
unlocked = true
- elseif self.unlock["DaemonicUnlock"] ~= nil and self.unlock.DaemonicUnlock() then
+ elseif br.unlock["DaemonicUnlock"] ~= nil and br.unlock:DaemonicUnlock() then
unlocked = true
end
- -- Set Spell Queue Window
- --if class == 8 or class == 9 then
- if unlocked and self.prevQueueWindow ~= 400 then
- b.RunMacroText("/console SpellQueueWindow 400")
+
+ if unlocked then
+ -- Set Spell Queue Window
+ --if class == 8 or class == 9 then
+ if unlocked and self.prevQueueWindow ~= 400 then
+ br._G.RunMacroText("/console SpellQueueWindow 400")
+ --else
+ -- if unlocked and self.prevQueueWindow ~= 0 then
+ -- br._G.RunMacroText("/console SpellQueueWindow 0")
+ -- end
+ -- end
+ end
+ -- UPDATE MEDIA PATH to use the copied Media folder in Interface\AddOns
+ local DiesalStyle = LibStub("DiesalStyle-1.0")
+ -- Use Interface\AddOns\Media\ which is copied during installation
+ local newPath = "Interface\\AddOns\\Media\\"
+ if DiesalStyle.SetMediaPath then
+ print("BadRotations: Unlocker loaded, updating Media path for DiesalStyle.")
+ DiesalStyle:SetMediaPath(newPath)
+ DiesalStyle:ReloadMedia()
+ end
end
- --else
- -- if unlocked and self.prevQueueWindow ~= 0 then
- -- b.RunMacroText("/console SpellQueueWindow 0")
- -- end
- -- end
return unlocked
end
@@ -28,10 +45,10 @@ end
local brlocVersion
local brcurrVersion
local brUpdateTimer
-function br:checkBrOutOfDate()
- brlocVersion = b.C_AddOns.GetAddOnMetadata(self.addonName, "Version")
- if (not brUpdateTimer or (b.GetTime() - brUpdateTimer) > 300) and self.player ~= nil then
- local startTime = b.debugprofilestop()
+function unlockers:checkBrOutOfDate()
+ brlocVersion = br._G.C_AddOns.GetAddOnMetadata(self.addonName, "Version")
+ if (not brUpdateTimer or (br._G.GetTime() - brUpdateTimer) > 300) and self.player ~= nil then
+ local startTime = br._G.debugprofilestop()
-- Request Current Version from GitHub
if b["EasyWoWToolbox"] ~= nil then -- EWT
--SendHTTPRequest('https://raw.githubusercontent.com/CuteOne/BadRotations/master/BadRotations.toc', nil,
@@ -39,7 +56,7 @@ function br:checkBrOutOfDate()
-- Check for commit updates from System/Updater.lua, which relies on EWT
self.updater:CheckOutdated()
- brUpdateTimer = b.GetTime()
+ brUpdateTimer = br._G.GetTime()
elseif b["wmbapi"] ~= nil then -- MB
local info = {
Url = "https://raw.githubusercontent.com/CuteOne/BadRotations/master/BadRotations.toc",
@@ -56,8 +73,8 @@ function br:checkBrOutOfDate()
-- Check against current version installed
if brlocVersion and brcurrVersion then
-- Print("Local: "..tostring(brlocVersion).." | Remote: "..tostring(brcurrVersion))
- local brcleanCurr = b.gsub(tostring(brcurrVersion), "%p", "")
- local brcleanLoc = b.gsub(tostring(brlocVersion), "%p", "")
+ local brcleanCurr = br._G.gsub(tostring(brcurrVersion), "%p", "")
+ local brcleanLoc = br._G.gsub(tostring(brlocVersion), "%p", "")
if tonumber(brcleanCurr) ~= tonumber(brcleanLoc) then
local msg =
"BadRotations is currently out of date. Local Version: " ..
@@ -66,11 +83,11 @@ function br:checkBrOutOfDate()
if self.isChecked("Overlay Messages") then
b["RaidNotice_AddMessage"](b["RaidWarningFrame"], msg, { r = 1, g = 0.3, b = 0.1 })
else
- b.print(msg)
+ br._G.print(msg)
end
end
end
- brUpdateTimer = b.GetTime()
+ brUpdateTimer = br._G.GetTime()
end
self.debug.cpu:updateDebug(startTime, "outOfDate")
end
diff --git a/System/Updater.lua b/System/Updater.lua
index 36dd3e7378..2e53d16e3f 100644
--- a/System/Updater.lua
+++ b/System/Updater.lua
@@ -17,25 +17,25 @@ local isInitialized = false
-- Utilities
----------------------------------------------------------------------------------------------------
local function IsSettingChecked()
- return br.isChecked("Auto Check for Updates")
+ return br.functions.misc:isChecked("Auto Check for Updates")
end
local function Print(msg)
if msg == nil then
return
end
- print(br.classColor .. "[BadRotations] |cffFFFFFF" .. msg)
+ print(br.ui.colors.class .. "[BadRotations] |cffFFFFFF" .. msg)
end
local function PrintError(msg)
if msg == nil then
return
end
- print(br.classColor .. "[BadRotations] |cffff6666" .. msg)
+ print(br.ui.colors.class .. "[BadRotations] |cffff6666" .. msg)
end
local function RaidWarning(message)
- if br.isChecked("Overlay Messages") then
+ if br.functions.misc:isChecked("Overlay Messages") then
br._G.RaidNotice_AddMessage(br._G.RaidWarningFrame, message, {r = 1, g = 0.3, b = 0.1})
end
end
diff --git a/System/UsefulFeatures.lua b/System/UsefulFeatures.lua
deleted file mode 100644
index 3520fe4e3e..0000000000
--- a/System/UsefulFeatures.lua
+++ /dev/null
@@ -1,275 +0,0 @@
-local _, br = ...
-function br:AcceptQueues()
- if br.getOptionCheck("Accept Queues") then
- -- Accept Queues
- br.randomReady = math.random(8, 15)
- -- add some randomness
- if br.readyToAccept and br.readyToAccept <= br._G.GetTime() - 5 then
- br._G.AcceptProposal()
- br.readyToAccept = nil
- br.randomReady = nil
- end
- end
-end
-
-------------------------------------------------------------------------------------------------------------------------
--- idTip by Silverwind
-local hooksecurefunc, select, UnitBuff, UnitDebuff, UnitAura, UnitGUID, _, tonumber, strfind =
- br._G.hooksecurefunc,
- br._G.select,
- br.UnitBuff,
- br._G.UnitDebuff,
- br._G.UnitAura,
- br._G.UnitGUID,
- br._G.GetGlyphSocketInfo,
- br._G.tonumber,
- br._G.strfind
-local types = {
- spell = "SpellID:",
- item = "ItemID:",
- glyph = "GlyphID:",
- unit = "NPC ID:",
- quest = "QuestID:",
- talent = "TalentID:",
- achievement = "AchievementID:"
-}
-local function addLine(tooltip, id, type, noEmptyLine)
- local found = false
- -- Check if we already added to this tooltip. Happens on the talent frame
- for i = 1, 15 do
- local frame = _G[tooltip:GetName() .. "TextLeft" .. i]
- local text
- if frame then
- text = frame:GetText()
- end
- if text and text == type then
- found = true
- break
- end
- end
- if not found then
- if not noEmptyLine then
- tooltip:AddLine(" ")
- end
- tooltip:AddDoubleLine(type, "|cffffffff" .. id)
- tooltip:Show()
- end
-end
--- All types, primarily for linked tooltips
-local function onSetHyperlink(self, link)
- local type, id = string.match(link, "^(%a+):(%d+)")
- if not type or not id then
- return
- end
- if type == "spell" or type == "enchant" or type == "trade" then
- addLine(self, id, types.spell)
- elseif type == "glyph" then
- addLine(self, id, types.glyph)
- elseif type == "talent" then
- addLine(self, id, types.talent)
- elseif type == "quest" then
- addLine(self, id, types.quest)
- elseif type == "achievement" then
- addLine(self, id, types.achievement)
- elseif type == "item" then
- addLine(self, id, types.item)
- end
-end
-hooksecurefunc(ItemRefTooltip, "SetHyperlink", onSetHyperlink)
-hooksecurefunc(GameTooltip, "SetHyperlink", onSetHyperlink)
--- Spells
-hooksecurefunc(
- br._G.GameTooltip,
- "SetUnitBuff",
- function(self, ...)
- local id = select(10, br._G.UnitBuff(...))
- if id then
- addLine(self, id, types.spell)
- end
- end
-)
-hooksecurefunc(
- br._G.GameTooltip,
- "SetUnitDebuff",
- function(self, ...)
- local id = select(10, br._G.UnitDebuff(...))
- if id then
- addLine(self, id, types.spell)
- end
- end
-)
-hooksecurefunc(
- br._G.GameTooltip,
- "SetUnitAura",
- function(self, ...)
- local unitAura = C_UnitAuras.GetAuraDataByIndex(...) --UnitAura(...)
- local id = unitAura and unitAura.spellId or nil
- if id then
- addLine(self, id, types.spell)
- end
- end
-)
-hooksecurefunc(
- "SetItemRef",
- function(link, ...)
- local id = tonumber(link:match("spell:(%d+)"))
- if id then
- addLine(br._G.ItemRefTooltip, id, types.spell)
- end
- end
-)
-TooltipDataProcessor.AddTooltipPostCall(Enum.TooltipDataType.Spell,
- function(self, data)
- -- local id = select(3, self:GetSpell())
- if data.id then
- addLine(self, data.id, types.spell)
- end
- end
-)
--- NPCs
-TooltipDataProcessor.AddTooltipPostCall(Enum.TooltipDataType.Unit,
- function(self)
- if br._G.C_PetBattles.IsInBattle() then
- return
- end
- local unit = select(2, self:GetUnit())
- if br.isChecked("Unit ID In Tooltip") and unit then
- local guid = UnitGUID(unit) or ""
- local id = tonumber(guid:match("-(%d+)-%x+$"), 10)
- local type = guid:match("%a+")
- -- ID 970 seems to be used for players
- if id and type ~= "Player" then
- addLine(br._G.GameTooltip, id, types.unit)
- end
- end
- end
-)
--- Items
-local function attachItemTooltip(self)
- if (self == GameTooltip or self == ItemRefTooltip) then
- local link = select(2, self:GetItem())
- if link then
- local id = select(3, strfind(link, "^|%x+|Hitem:(%-?%d+):(%d+):(%d+).*"))
- if id then
- addLine(self, id, types.item)
- end
- end
- end
-end
-TooltipDataProcessor.AddTooltipPostCall(Enum.TooltipDataType.Item, attachItemTooltip)
--- Glyphs -- Commented out due to Legion
--- hooksecurefunc(GameTooltip, "SetGlyph", function(self, ...)
--- local id = select(4, GetGlyphSocketInfo(...))
--- if id then addLine(self, id, types.glyph) end
--- end)
--- hooksecurefunc(GameTooltip, "SetGlyphByID", function(self, id)
--- if id then addLine(self, id, types.glyph) end
--- end)
--- Achievement Frame Tooltips
--- local f = br._G.CreateFrame("frame")
--- f:RegisterEvent("ADDON_LOADED")
--- f:SetScript(
--- "OnEvent",
--- function(_, _, what)
--- if what == "Blizzard_AchievementUI" then
--- for _, button in ipairs(br._G.AchievementFrameAchievements.ScrollBox.ScrollTarget:GetChildren()) do
--- button:HookScript(
--- "OnEnter",
--- function()
--- br._G.GameTooltip:SetOwner(button, "ANCHOR_NONE")
--- br._G.GameTooltip:SetPoint("TOPLEFT", button, "TOPRIGHT", 0, 0)
--- addLine(br._G.GameTooltip, button.id, types.achievement, true)
--- br._G.GameTooltip:Show()
--- end
--- )
--- button:HookScript(
--- "OnLeave",
--- function()
--- br._G.GameTooltip:Hide()
--- end
--- )
--- end
--- end
--- end
--- )
--- local petAbilityTooltipID = false
--- local orig_SharedPetBattleAbilityTooltip_SetAbility = br._G.SharedPetBattleAbilityTooltip_SetAbility
--- function br.SharedPetBattleAbilityTooltip_SetAbility(self, abilityInfo, additionalText)
--- orig_SharedPetBattleAbilityTooltip_SetAbility(self, abilityInfo, additionalText)
--- petAbilityTooltipID = abilityInfo:GetAbilityID()
--- end
--- br._G.PetBattlePrimaryAbilityTooltip:HookScript(
--- "OnShow",
--- function(self)
--- local name = self.Name:GetText()
--- self.Name:SetText(name .. " (ID: " .. petAbilityTooltipID .. ")")
--- end
--- )
-------------------------------------------------------------------------------------------------------------------------
--- LibStub
--- $Id: LibStub.lua 76 2007-09-03 01:50:17Z mikk $
--- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info
--- LibStub is hereby placed in the Public Domain
--- Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
-local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
-local LibStub = _G[LIBSTUB_MAJOR]
--- Check to see is this version of the stub is obsolete
-if not LibStub or LibStub.minor < LIBSTUB_MINOR then
- LibStub = LibStub or { libs = {}, minors = {} }
- _G[LIBSTUB_MAJOR] = LibStub
- LibStub.minor = LIBSTUB_MINOR
- -- LibStub:NewLibrary(major, minor)
- -- major (string) - the major version of the library
- -- minor (string or number ) - the minor version of the library
- --
- -- returns nil if a newer or same version of the lib is already present
- -- returns empty library object or old library object if upgrade is needed
- function LibStub:NewLibrary(major, minor)
- assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
- minor = assert(tonumber(br._G.strmatch(minor, "%d+")),
- "Minor version must either be a number or contain a number.")
- local oldminor = self.minors[major]
- if oldminor and oldminor >= minor then
- return nil
- end
- self.minors[major], self.libs[major] = minor, self.libs[major] or {}
- return self.libs[major], oldminor
- end
-
- -- LibStub:GetLibrary(major, [silent])
- -- major (string) - the major version of the library
- -- silent (boolean) - if true, library is optional, silently return nil if its not found
- --
- -- throws an error if the library can not be found (except silent is set)
- -- returns the library object if found
- function LibStub:GetLibrary(major, silent)
- if not self.libs[major] and not silent then
- error(("Cannot find a library instance of %q."):format(tostring(major)), 2)
- end
- return self.libs[major], self.minors[major]
- end
-
- -- LibStub:IterateLibraries()
- --
- -- Returns an iterator for the currently registered libraries
- function LibStub:IterateLibraries()
- return pairs(self.libs)
- end
-
- setmetatable(LibStub, { __call = LibStub.GetLibrary })
-end
-
-function br.deepcopy(orig)
- local orig_type = type(orig)
- local copy
- if orig_type == "table" then
- copy = {}
- for orig_key, orig_value in next, orig, nil do
- copy[br.deepcopy(orig_key)] = br.deepcopy(orig_value)
- end
- setmetatable(copy, br.deepcopy(getmetatable(orig)))
- else -- number, string, boolean, etc
- copy = orig
- end
- return copy
-end
diff --git a/System/engines/AuraTracker.lua b/System/engines/AuraTracker.lua
index 3c2698e7d1..d804a797ab 100644
--- a/System/engines/AuraTracker.lua
+++ b/System/engines/AuraTracker.lua
@@ -1,4 +1,9 @@
local _, br = ...
+br.engines.auraTracker = br.engines.auraTracker or {}
+local auraTracker = br.engines.auraTracker
+
+auraTracker.units = auraTracker.units or {}
+
-- ProbablyEngine Rotations
-- Released under modified BSD, see attached LICENSE.
@@ -6,42 +11,30 @@ local _, br = ...
-- units = { }
-- })
-if br.tracker == nil then br.tracker = {} end
-if br.tracker ~= nil then
- if br.tracker.units == nil then br.tracker.units = {} end
-end
-
--- local DiesalGUI = LibStub("DiesalGUI-1.0")
--- local explore = DiesalGUI:Create('TableExplorer')
--- explore:SetTable("Aura Tracker", br.tracker.units, 5)
-
--- local tracker = ProbablyEngine.module.tracker
-local tracker = br.tracker
-
-function tracker.add(guid, name, spellId, time) --Check if exists and creates if not
- if not tracker.units[guid] then -- Does table exists else create (same as self.(de)buff[k][thisUnit])
- tracker.units[guid] = {
+function auraTracker:add(guid, name, spellId, time) --Check if exists and creates if not
+ if not auraTracker.units[guid] then -- Does table exists else create (same as self.(de)buff[k][thisUnit])
+ auraTracker.units[guid] = {
guid = guid,
name = name,
auras = { }
}
end
- local unit = tracker.units[guid] -- unit shortcut
- if br.GetObjectExists(unit.guid) then
+ local unit = auraTracker.units[guid] -- unit shortcut
+ if br.functions.unit:GetObjectExists(unit.guid) then
br.objectID = br._G.GetObjectWithGUID(unit.guid)
- elseif br.GetObjectExists("target") then
+ elseif br.functions.unit:GetObjectExists("target") then
br.objectID = br._G.GetObjectWithGUID(br._G.UnitGUID("target"))
else
br.objectID = br._G.GetObjectWithGUID(br._G.UnitGUID("player"))
end
if not unit.auras[spellId] then
unit.auras[spellId] = {
- spellName = br._G.GetSpellInfo(spellId), --GetSpellName(spellId),
+ spellName = br.api.wow.GetSpellInfo(spellId), --GetSpellName(spellId),
id = spellId,
- duration = br.getAuraDuration(br.objectID, spellId),
- remain = br.getAuraRemain(br.objectID, spellId),
- refresh = br.getAuraRemain(br.objectID, spellId) < br.getAuraDuration(br.objectID, spellId) * 0.3,
- stacks = br.getAuraStacks(br.objectID, spellId),
+ duration = br.functions.aura:getAuraDuration(br.objectID, spellId),
+ remain = br.functions.aura:getAuraRemain(br.objectID, spellId),
+ refresh = br.functions.aura:getAuraRemain(br.objectID, spellId) < br.functions.aura:getAuraDuration(br.objectID, spellId) * 0.3,
+ stacks = br.functions.aura:getAuraStacks(br.objectID, spellId),
stack = 0,
time = time
}
@@ -49,9 +42,9 @@ function tracker.add(guid, name, spellId, time) --Check if exists and creates if
end
end
-function tracker.remove(guid, spellId)
- if tracker.units[guid] then
- local unit = tracker.units[guid]
+function auraTracker.remove(guid, spellId)
+ if auraTracker.units[guid] then
+ local unit = auraTracker.units[guid]
if unit.auras[spellId] then
unit.auras[spellId] = nil
if br.explore then br.explore:BuildTree() end
@@ -59,9 +52,9 @@ function tracker.remove(guid, spellId)
end
end
-function tracker.update(type, guid, spellId, amount, crit, name)
- if tracker.units[guid] then
- local unit = tracker.units[guid]
+function auraTracker.update(type, guid, spellId, amount, crit, name)
+ if auraTracker.units[guid] then
+ local unit = auraTracker.units[guid]
if unit.auras[spellId] then
local spell = unit.auras[spellId]
-- -- spell.remain = getAuraRemain(name, spellId)
@@ -104,15 +97,15 @@ function tracker.update(type, guid, spellId, amount, crit, name)
end
end
-function tracker.onUpdate()
- if tracker.units ~= nil then
- for k, _ in pairs(tracker.units) do
- local unit = tracker.units[k]
+function auraTracker.onUpdate()
+ if auraTracker.units ~= nil then
+ for k, _ in pairs(auraTracker.units) do
+ local unit = auraTracker.units[k]
-- local name = unit.name
local auras = unit.auras
- if br.GetObjectExists(unit.guid) then
+ if br.functions.unit:GetObjectExists(unit.guid) then
br.objectID = br._G.GetObjectWithGUID(unit.guid)
- elseif br.GetObjectExists("target") then
+ elseif br.functions.unit:GetObjectExists("target") then
br.objectID = br._G.GetObjectWithGUID(br._G.UnitGUID("target"))
else
br.objectID = br._G.GetObjectWithGUID(br._G.UnitGUID("player"))
@@ -120,7 +113,7 @@ function tracker.onUpdate()
if auras ~= nil then
for c, _ in pairs(auras) do
local spell = auras[c]
- spell.remain = br.getAuraRemain(br.objectID,c)
+ spell.remain = br.functions.aura:getAuraRemain(br.objectID,c)
spell.refresh = spell.remain < spell.duration * 0.3
end
end
@@ -128,9 +121,9 @@ function tracker.onUpdate()
end
end
-function tracker.stack(guid, spellId, amount)
- if tracker.units[guid] then
- local unit = tracker.units[guid]
+function auraTracker.stack(guid, spellId, amount)
+ if auraTracker.units[guid] then
+ local unit = auraTracker.units[guid]
if unit.auras[spellId] then
unit.auras[spellId].stacks = amount
if br.explore then br.explore:BuildTree() end
@@ -138,9 +131,9 @@ function tracker.stack(guid, spellId, amount)
end
end
-function tracker.query(guid, spellId)
- if tracker.units[guid] then
- local unit = tracker.units[guid]
+function auraTracker.query(guid, spellId)
+ if auraTracker.units[guid] then
+ local unit = auraTracker.units[guid]
if unit.auras[spellId] then
return unit.auras[spellId]
end
@@ -148,33 +141,33 @@ function tracker.query(guid, spellId)
return false
end
-function tracker.handleEvent(...) --Adjust handleEvent to CombatLogEventUnfiltered
+function auraTracker.handleEvent(...) --Adjust handleEvent to CombatLogEventUnfiltered
local timeStamp, event, _, _, _, _,
_, destGUID, destName, _, _ = ...
-- add aura
if event == "SPELL_AURA_APPLIED" or event == "SPELL_AURA_REFRESH" or event == "SPELL_PERIODIC_AURA_APPLIED" then
local spellId, _, _ = select(12, ...)
- tracker.add(destGUID, destName, spellId, timeStamp)
+ auraTracker.add(destGUID, destName, spellId, timeStamp)
-- remove aura
elseif event == "SPELL_AURA_REMOVED" or event == "SPELL_PERIODIC_AURA_REMOVED" then
local spellId, _, _ = select(12, ...)
- tracker.remove(destGUID, spellId)
+ auraTracker.remove(destGUID, spellId)
elseif event == "SPELL_AURA_APPLIED_DOSE" or event == "SPELL_PERIODIC_AURA_APPLIED_DOSE"
or event == "SPELL_AURA_REMOVED_DOSE" or event == "SPELL_PERIODIC_AURA_REMOVED_DOSE" then
local spellId, _, _ = select(12, ...)
local _, amount = select(15, ...)
- tracker.stack(destGUID, spellId, amount)
+ auraTracker.stack(destGUID, spellId, amount)
-- aura damage
elseif event == "SPELL_PERIODIC_DAMAGE" then
local spellId, _, _ = select(12, ...)
local amount, _, _, _, _, _, critical = select(15, ...)
- tracker.update('damage', destGUID, spellId, amount, critical, destName)
+ auraTracker.update('damage', destGUID, spellId, amount, critical, destName)
elseif event == "SPELL_PERIODIC_HEAL" then
local spellId, _, _ = select(12, ...)
local amount, _, _, _, _, _, critical = select(15, ...)
- tracker.update('heal', destGUID, spellId, amount, critical, destName)
+ auraTracker.update('heal', destGUID, spellId, amount, critical, destName)
end
end
\ No newline at end of file
diff --git a/System/engines/EnemiesEngine.lua b/System/engines/EnemiesEngine.lua
index 22df44d122..6f18d50ad1 100644
--- a/System/engines/EnemiesEngine.lua
+++ b/System/engines/EnemiesEngine.lua
@@ -1,101 +1,104 @@
local _, br = ...
------------------------------------------Bubba's Healing Engine--------------------------------------]]
+br.engines.enemiesEngine = br.engines.enemiesEngine or {}
+local enemiesEngine = br.engines.enemiesEngine
+
+-- Shared debuff lookup: module-scoped to avoid allocating a new closure per cached debuff entry
+local function findDebuffOnUnit(buffName, unit)
+ return br.api.wow.FindAuraByName(br.api.wow.GetSpellInfo(buffName), unit, "HARMFUL|PLAYER")
+end
+
+-----------------------------------------Bubba's Healing Engine--------------------------------------
--Modified to enemies engine by fisker
-if not br.metaTable2 then
+if not enemiesEngine.metaTable2 then
-- localizing the commonly used functions while inside loops
local tinsert, tremove, GetTime = br._G.tinsert, br._G.tremove, br._G.GetTime
local pX, pY, pZ, pCR, autoLoot
- br.om = {} -- This is our main Table that the world will see
- br.ttd = {} -- Time to die table
- br.unitSetup = {} -- This is one of our MetaTables that will be the default user/contructor
- br.unitSetup.cache = {} -- This is for the cache Table to check against
- br.unitBlacklist = { -- blacklist for units
+ enemiesEngine.om = {} -- This is our main Table that the world will see
+ enemiesEngine.ttd = {} -- Time to die table
+ enemiesEngine.unitSetup = {} -- This is one of our MetaTables that will be the default user/contructor
+ enemiesEngine.unitSetup.cache = {} -- This is for the cache Table to check against
+ enemiesEngine.unitBlacklist = { -- blacklist for units
[129359] = true, -- Sawtooth Shark
[129448] = true, -- Hammer Shark
[144942] = true, -- Spark Bot
}
- br.metaTable2 = {} -- This will be the MetaTable attached to our Main Table that the world will see
- br.metaTable2.__index = { -- Setting the Metamethod of Index for our Main Table
+ enemiesEngine.metaTable2 = {} -- This will be the MetaTable attached to our Main Table that the world will see
+ enemiesEngine.metaTable2.__index = { -- Setting the Metamethod of Index for our Main Table
name = "Enemies Table",
author = "Bubba",
}
-- Creating a default Unit to default to on a check
- br.unitSetup.__index = {
+ enemiesEngine.unitSetup.__index = {
name = "dangerousbeast",
hp = 100,
unit = "noob",
guid = 0,
guidsh = 0,
range = 100,
- debuffs = {},
timestamp = GetTime(),
}
- function br.unitSetup:new(unit)
+ local unitIndex = enemiesEngine.unitSetup.__index
+
+ -- Function time to die
+ function unitIndex:unitTtd(targetPercentage)
local startTime = br._G.debugprofilestop()
- -- Seeing if we have already cached this unit before
- if br.unitSetup.cache[unit] then return false end
- if br.UnitDebuffID("player", 295249) and br._G.UnitIsPlayer(unit) then return false end
- if br.unitBlacklist[br.GetObjectID(unit)] then return false end
- local o = {}
- setmetatable(o, br.unitSetup)
- if unit and (type(unit) == "string" or (br.unlocker == "NN" and type(unit) == "number")) then
- o.unit = unit
+ if targetPercentage == nil then targetPercentage = 0 end
+ local value
+ if self.hp == 0 then return -1 end
+ if self.hp == 100 or br.functions.unit:isDummy(self.unit) then return 999 end
+ local timeNow = GetTime()
+ -- Reset unit if HP is higher
+ if enemiesEngine.ttd[self.unit] ~= nil and (enemiesEngine.ttd[self.unit].lasthp < self.hp or #enemiesEngine.ttd[self.unit].values == 0) then
+ enemiesEngine.ttd[self.unit] = nil
end
- --Function time to die
- function o:unitTtd(targetPercentage)
- local startTime = br._G.debugprofilestop()
- if targetPercentage == nil then targetPercentage = 0 end
- local value
- if o.hp == 0 then return -1 end
- if o.hp == 100 or br.isDummy(o.unit) then return 999 end
- local timeNow = GetTime()
- -- Reset unit if HP is higher
- if br.ttd[o.unit] ~= nil and (br.ttd[o.unit].lasthp < o.hp or #br.ttd[o.unit].values == 0) then
- br.ttd[o.unit] = nil
- end
- -- initialize new unit
- if br.ttd[o.unit] == nil then
- br.ttd[o.unit] = {} -- create unit
- br.ttd[o.unit].values = {} -- create value table
- value = { time = 0, hp = o.hp } -- create initial values
- tinsert(br.ttd[o.unit].values, 1, value) -- insert unit
- br.ttd[o.unit].lasthp = o.hp -- store current hp pct
- br.ttd[o.unit].startTime = timeNow -- store current time
- br.ttd[o.unit].lastTime = 0 --store last time value
- return 999
- end
- local ttdUnit = br.ttd[o.unit]
- -- add current value to ttd table if HP changed or more than X sec since last update
- if o.hp ~= ttdUnit.lasthp or (timeNow - ttdUnit.startTime - ttdUnit.lastTime) > 0.5 then
- value = { time = timeNow - ttdUnit.startTime, hp = o.hp }
- tinsert(ttdUnit.values, 1, value)
- br.ttd[o.unit].lasthp = o.hp
- br.ttd[o.unit].lastTime = timeNow - ttdUnit.startTime
- end
- -- clean units
- local valueCount = #ttdUnit.values
- while valueCount > 0 and (valueCount > 100 or (timeNow - ttdUnit.startTime - ttdUnit.values[valueCount].time) > 10) do
- ttdUnit.values[valueCount] = nil
- valueCount = valueCount - 1
+ -- initialize new unit
+ if enemiesEngine.ttd[self.unit] == nil then
+ enemiesEngine.ttd[self.unit] = {} -- create unit
+ enemiesEngine.ttd[self.unit].values = {} -- create value table
+ value = { time = 0, hp = self.hp } -- create initial values
+ tinsert(enemiesEngine.ttd[self.unit].values, 1, value) -- insert unit
+ enemiesEngine.ttd[self.unit].lasthp = self.hp -- store current hp pct
+ enemiesEngine.ttd[self.unit].startTime = timeNow -- store current time
+ enemiesEngine.ttd[self.unit].lastTime = 0 --store last time value
+ return 999
+ end
+ local ttdUnit = enemiesEngine.ttd[self.unit]
+ -- add current value to ttd table if HP changed or more than X sec since last update
+ if self.hp ~= ttdUnit.lasthp or (timeNow - ttdUnit.startTime - ttdUnit.lastTime) > 0.5 then
+ value = { time = timeNow - ttdUnit.startTime, hp = self.hp }
+ tinsert(ttdUnit.values, 1, value)
+ enemiesEngine.ttd[self.unit].lasthp = self.hp
+ enemiesEngine.ttd[self.unit].lastTime = timeNow - ttdUnit.startTime
+ end
+ -- clean units
+ local valueCount = #ttdUnit.values
+ while valueCount > 0 and (valueCount > 100 or (timeNow - ttdUnit.startTime - ttdUnit.values[valueCount].time) > 10) do
+ ttdUnit.values[valueCount] = nil
+ valueCount = valueCount - 1
+ end
+ -- limit samples used for regression to avoid long computation in combat UI
+ valueCount = #ttdUnit.values
+ if valueCount > 1 then
+ local maxSamples = 20
+ local samples = math.min(valueCount, maxSamples)
+ -- linear regression calculation from https://github.com/herotc/hero-lib/
+ local a, b = 0, 0
+ local Ex2, Ex, Exy, Ey = 0, 0, 0, 0
+ local x, y
+ -- use most recent 'samples' entries (values are inserted at index 1)
+ for i = 1, samples do
+ x, y = ttdUnit.values[i].time, ttdUnit.values[i].hp
+ Ex2 = Ex2 + x * x
+ Ex = Ex + x
+ Exy = Exy + x * y
+ Ey = Ey + y
end
- -- calculate ttd if more than 3 values
- valueCount = #ttdUnit.values
- if valueCount > 1 then
- -- linear regression calculation from https://github.com/herotc/hero-lib/
- local a, b = 0, 0
- local Ex2, Ex, Exy, Ey = 0, 0, 0, 0
- local x, y
- for i = 1, valueCount do
- x, y = ttdUnit.values[i].time, ttdUnit.values[i].hp
- Ex2 = Ex2 + x * x
- Ex = Ex + x
- Exy = Exy + x * y
- Ey = Ey + y
- end
- local invariant = 1 / (Ex2 * valueCount - Ex * Ex)
+ local denom = (Ex2 * samples - Ex * Ex)
+ if denom ~= 0 then
+ local invariant = 1 / denom
a = (-Ex * Exy * invariant) + (Ex2 * Ey * invariant)
- b = (valueCount * Exy * invariant) - (Ex * Ey * invariant)
+ b = (samples * Exy * invariant) - (Ex * Ey * invariant)
if b ~= 0 then
local ttdSec = (targetPercentage - a) / b
ttdSec = math.min(999, ttdSec - (timeNow - ttdUnit.startTime))
@@ -105,217 +108,367 @@ if not br.metaTable2 then
return -1 -- TTD under 0
end
end
- -- Debugging
- br.debug.cpu:updateDebug(startTime, "enemiesEngine.unitSetup.ttd")
- return 999 -- not enough values
end
+ -- Debugging
+ br.debug.cpu:updateDebug(startTime, "enemiesEngine.unitSetup.ttd")
+ return 999 -- not enough values
+ end
- --Distance
- function o:RawDistance()
- local x1, y1, z1 = pX, pY, pZ
- local x2, y2, z2 = o.posX, o.posY, o.posZ
- if x1 == nil or x2 == nil or y1 == nil or y2 == nil or z1 == nil or z2 == nil then
- return 99
- else
- return math.sqrt(((x2 - x1) ^ 2) + ((y2 - y1) ^ 2) + ((z2 - z1) ^ 2)) -
- ((pCR or 0) + (br._G.UnitCombatReach(o.unit) or 0)), z2 - z1
- end
+ -- Distance
+ function unitIndex:RawDistance()
+ local x1, y1, z1 = pX, pY, pZ
+ local x2, y2, z2 = self.posX, self.posY, self.posZ
+ if x1 == nil or x2 == nil or y1 == nil or y2 == nil or z1 == nil or z2 == nil then
+ return 99
+ else
+ return math.sqrt(((x2 - x1) ^ 2) + ((y2 - y1) ^ 2) + ((z2 - z1) ^ 2)) -
+ ((pCR or 0) + (br._G.UnitCombatReach(self.unit) or 0)), z2 - z1
end
+ end
- --Add unit to table
- function o:AddUnit(table)
- local thisUnit
- if br._G.UnitIsOtherPlayersPet(o.unit) then
- thisUnit = {
- unit = o.unit,
- }
- else
- thisUnit = {
- unit = o.unit,
- name = o.name,
- guid = o.guid,
- id = o.objectID,
- range = o.range,
- debuffs = o.debuffs,
- timestamp = GetTime(),
- }
- end
- -- br._G.print("Adding "..thisUnit.unit.." to table")
- rawset(table, o.unit, thisUnit)
+ -- Add unit to table
+ function unitIndex:AddUnit(table)
+ local thisUnit
+ if br._G.UnitIsOtherPlayersPet(self.unit) then
+ thisUnit = {
+ unit = self.unit,
+ }
+ else
+ thisUnit = {
+ unit = self.unit,
+ name = self.name,
+ guid = self.guid,
+ id = self.objectID,
+ range = self.range,
+ debuffs = self.debuffs,
+ timestamp = GetTime(),
+ }
end
+ -- br._G.print("Adding "..thisUnit.unit.." to table")
+ rawset(table, self.unit, thisUnit)
+ end
- --Debuffs
- function o:UpdateDebuffs(debuffList, unit)
- local startTime = br._G.debugprofilestop()
- if not br.isChecked("Cache Debuffs") then
- debuffList = {}
- return debuffList
- end
- local tracker
- local buffCaster
- local buffName
- local buffUnit
- -- Add Debuffs
- local function cacheDebuff(buffUnit, buffName, buffCaster)
- -- Print("Caching Debuff!")
- -- Cache it to the OM
- if buffCaster ~= nil and buffCaster == "player" then --(buffCaster == "player" or UnitIsFriend("player",buffCaster)) then
- if debuffList[buffCaster] == nil then debuffList[buffCaster] = {} end
- if debuffList[buffCaster][buffName] == nil then
- -- Print("Adding player debuff")
- debuffList[buffCaster][buffName] = function(buffName, unit)
- return br._G.AuraUtil.FindAuraByName(br._G.GetSpellInfo(buffName), buffUnit, "HARMFUL|PLAYER")
- end
- if debuffList[buffCaster][buffName] ~= nil then br.read.debuffTracker[unit][buffName] = nil end
- end
+ -- Debuffs
+ function unitIndex:UpdateDebuffs(debuffList, unit)
+ local startTime = br._G.debugprofilestop()
+ if not br.functions.misc:isChecked("Cache Debuffs") then
+ debuffList = {}
+ return debuffList
+ end
+ local tracker
+ local buffCaster
+ local buffName
+ local buffUnit
+ -- Add Debuffs
+ local function cacheDebuff(buffUnit, buffName, buffCaster)
+ -- Cache it to the OM
+ if buffCaster ~= nil and buffCaster == "player" then
+ if debuffList[buffCaster] == nil then debuffList[buffCaster] = {} end
+ if debuffList[buffCaster][buffName] == nil then
+ debuffList[buffCaster][buffName] = findDebuffOnUnit
+ if debuffList[buffCaster][buffName] ~= nil then br.readers.combatLog.debuffTracker[unit][buffName] = nil end
end
end
- -- Get the Info from Combat Log
- for k, _ in pairs(br.read.debuffTracker) do
- tracker = br.read.debuffTracker[k]
- for j, _ in pairs(tracker) do
- buffCaster = tracker[j][1]
- buffName = tracker[j][2]
- buffUnit = tracker[j][3]
- if buffUnit == unit and (debuffList[buffCaster] == nil or debuffList[buffCaster][buffName] == nil) then
- cacheDebuff(buffUnit, buffName, buffCaster)
- end
+ end
+ -- Get the Info from Combat Log
+ for k, _ in pairs(br.readers.combatLog.debuffTracker) do
+ tracker = br.readers.combatLog.debuffTracker[k]
+ for j, _ in pairs(tracker) do
+ buffCaster = tracker[j][1]
+ buffName = tracker[j][2]
+ buffUnit = tracker[j][3]
+ if buffUnit == unit and (debuffList[buffCaster] == nil or debuffList[buffCaster][buffName] == nil) then
+ cacheDebuff(buffUnit, buffName, buffCaster)
end
end
- -- Remove Debuffs
- for buffCaster, buffs in pairs(debuffList) do
- for buffName, _ in pairs(buffs) do
- if debuffList[buffCaster][buffName] ~= nil then
- if debuffList[buffCaster][buffName](buffName, unit) == nil then
- -- Print("Removing player expired - "..buffName)
- debuffList[buffCaster][buffName] = nil
- if br.read.debuffTracker[unit] ~= nil and br.read.debuffTracker[unit][buffName] ~= nil and br.read.debuffTracker[unit][buffName][1] == buffCaster then
- br.read.debuffTracker[unit][buffName] = nil
- end
+ end
+ -- Remove Debuffs
+ for buffCaster, buffs in pairs(debuffList) do
+ for buffName, _ in pairs(buffs) do
+ if debuffList[buffCaster][buffName] ~= nil then
+ if debuffList[buffCaster][buffName](buffName, unit) == nil then
+ debuffList[buffCaster][buffName] = nil
+ if br.readers.combatLog.debuffTracker[unit] ~= nil and br.readers.combatLog.debuffTracker[unit][buffName] ~= nil and br.readers.combatLog.debuffTracker[unit][buffName][1] == buffCaster then
+ br.readers.combatLog.debuffTracker[unit][buffName] = nil
end
end
end
end
- -- Debugging
- br.debug.cpu:updateDebug(startTime, "enemiesEngine.unitSetup.updateDebuffs")
- return debuffList
end
+ -- Debugging
+ br.debug.cpu:updateDebug(startTime, "enemiesEngine.unitSetup.updateDebuffs")
+ return debuffList
+ end
- -- Updating the values of the Unit
- function o:UpdateUnit()
- -- br._G.print("Updating "..o.unit)
- local startTime = br._G.debugprofilestop()
- o.posX, o.posY, o.posZ = br._G.ObjectPosition(o.unit)
- o.name = br._G.UnitName(o.unit)
- o.guid = br._G.UnitGUID(o.unit)
- o.distance = o:RawDistance()
- o.hpabs = br._G.UnitHealth(o.unit)
- o.hpmax = br._G.UnitHealthMax(o.unit)
- o.hp = o.hpabs / o.hpmax * 100
- o.objectID = br._G.ObjectID(o.unit)
- o.range = o.range or 0
- o.debuffs = o.debuffs or {}
- if o.distance <= 50 and not br.GetUnitIsDeadOrGhost(o.unit) and not br.isCritter(o.unit) then
- -- EnemyListCheck
- if o.enemyRefresh == nil or o.enemyRefresh < GetTime() - 1 then
- o.enemyListCheck = br.enemyListCheck(o.unit)
- o.enemyRefresh = GetTime()
- if o.enemyListCheck == true then
- o.range = br.getDistanceCalc(o.unit)
- if br.units[o.unit] == nil then
- o:AddUnit(br.units)
- end
- br.units[o.unit].range = o.range
- else
- if br.units[o.unit] ~= nil then
- br.units[o.unit] = nil
- end
- end
+ -- Updating the values of the Unit
+ function unitIndex:UpdateUnit()
+ local startTime = br._G.debugprofilestop()
+ -- Localize hot APIs for this update to reduce table lookups
+ local ObjectPosition = br._G.ObjectPosition
+ local UnitName = br._G.UnitName
+ local UnitGUID = br._G.UnitGUID
+ local UnitHealth = br._G.UnitHealth
+ local UnitHealthMax = br._G.UnitHealthMax
+ local ObjectPointer = br._G.ObjectPointer
+ local UnitCanAttack = br._G.UnitCanAttack
+ local UnitAffectingCombat = br._G.UnitAffectingCombat
+ local GetTime = br._G.GetTime
+
+ -- Cache position first (used multiple times below)
+ self.posX, self.posY, self.posZ = ObjectPosition(self.unit)
+
+ -- Early exit if unit has no position (invalid)
+ if not self.posX then
+ return
+ end
+
+ self.name = UnitName(self.unit)
+ self.guid = UnitGUID(self.unit)
+ self.distance = self:RawDistance()
+ self.hpabs = UnitHealth(self.unit)
+ self.hpmax = UnitHealthMax(self.unit)
+ self.hp = self.hpabs / self.hpmax * 100
+ self.objectID = br._G.ObjectID(self.unit)
+ self.range = self.range or 0
+ self.debuffs = self.debuffs or {}
+
+ -- Check if this unit is in damaged table (bypass normal validation)
+ local unitPointer = ObjectPointer(self.unit)
+ local isDamagedUnit = br.engines.enemiesEngine.damaged and br.engines.enemiesEngine.damaged[unitPointer] ~= nil
+
+ if self.distance <= 50 and not br.functions.unit:GetUnitIsDeadOrGhost(self.unit) and not br.functions.unit:isCritter(self.unit) then
+ -- EnemyListCheck
+ -- FPS-adaptive refresh rate for optimal responsiveness without sacrificing performance
+ local fps = br.engines.enemiesEngine.cachedFPS or 60
+ local refreshInterval = 1 -- Default: solo content
+ if br._G.GetNumGroupMembers() > 0 then
+ if fps >= 60 then
+ refreshInterval = 0.25 -- Excellent FPS: maximum responsiveness
+ elseif fps >= 45 then
+ refreshInterval = 0.4 -- Good FPS: balanced
+ elseif fps >= 30 then
+ refreshInterval = 0.6 -- Acceptable FPS: reduce load
+ else
+ refreshInterval = 0.8 -- Low FPS: prioritize performance over responsiveness
end
- else
- o.enemyListCheck = false
- if br.units[o.unit] ~= nil then
- br.units[o.unit] = nil
+ end
+
+ -- Combat-reactive refresh rates for better responsiveness
+ local isAlreadyValid = br.engines.enemiesEngine.units[self.unit] ~= nil
+ local actualRefreshInterval
+
+ if UnitAffectingCombat("player") then
+ local needsFastValidation = false
+ if isDamagedUnit then
+ needsFastValidation = true
+ elseif br.functions.misc:isTargeting(self.unit) then
+ needsFastValidation = true
+ elseif not isAlreadyValid and br._G.GetNumGroupMembers() > 0 then
+ needsFastValidation = br.functions.combat:hasThreat(self.unit)
+ end
+
+ if needsFastValidation then
+ actualRefreshInterval = 0.05
+ elseif isAlreadyValid then
+ actualRefreshInterval = refreshInterval * 1.1
+ else
+ actualRefreshInterval = refreshInterval
end
+ elseif isAlreadyValid then
+ actualRefreshInterval = refreshInterval * 1.25
+ else
+ actualRefreshInterval = refreshInterval
end
- -- Is valid unit - only check if enemyList checks out
- if o.enemyListCheck == true then
- o.isValidUnit = br.isValidUnit(o.unit)
- if o.isValidUnit == true then
- o.debuffs = o:UpdateDebuffs(o.debuffs, o.unit)
- -- o.range = getDistanceCalc(o.unit)
- if br.enemy[o.unit] == nil then
- o:AddUnit(br.enemy)
+
+ if self.enemyRefresh == nil or self.enemyRefresh < GetTime() - actualRefreshInterval then
+ if isDamagedUnit then
+ self.enemyListCheck = true
+ else
+ self.enemyListCheck = br.functions.misc:enemyListCheck(self.unit)
+ end
+ self.enemyRefresh = GetTime()
+ if self.enemyListCheck == true then
+ self.range = br.functions.range:getDistanceCalc(self.unit)
+ if br.engines.enemiesEngine.units[self.unit] == nil then
+ self:AddUnit(br.engines.enemiesEngine.units)
end
- -- br.enemy[o.unit].range = o.
- br.enemy[o.unit].debuffs = o.debuffs
+ br.engines.enemiesEngine.units[self.unit].range = self.range
else
- if br.enemy[o.unit] ~= nil then
- br.enemy[o.unit] = nil
+ if br.engines.enemiesEngine.units[self.unit] ~= nil then
+ br.engines.enemiesEngine.units[self.unit] = nil
end
- if br.damaged ~= nil and br.damaged[o.unit] ~= nil then br.damaged[o.unit] = nil end
end
- else
- o.isValidUnit = false
- if br.enemy[o.unit] ~= nil then
- br.enemy[o.unit] = nil
+ end
+ else
+ self.enemyListCheck = false
+ if br.engines.enemiesEngine.units[self.unit] ~= nil then
+ br.engines.enemiesEngine.units[self.unit] = nil
+ end
+ end
+
+ -- Fast invalidation path (already-valid enemies):
+ -- Goal: reflect CC/no-touch/unattackable state ASAP without re-running full validation every pulse.
+ -- This only runs for units currently in the enemy table and uses a very short TTL to cap aura scan cost.
+ local isAlreadyEnemy = br.engines.enemiesEngine.enemy[self.unit] ~= nil
+ if isAlreadyEnemy and self.enemyListCheck == true and not isDamagedUnit then
+ local now = GetTime()
+ local fastTTL = UnitAffectingCombat("player") and 0.05 or 0.10
+ if self.fastInvalidationRefresh == nil or self.fastInvalidationRefresh < (now - fastTTL) then
+ local ok = true
+ -- Basic cheap guards (avoid waiting for enemyListCheck refresh)
+ if self.hpabs <= 0 or br.functions.unit:GetUnitIsDeadOrGhost(self.unit) then
+ ok = false
+ elseif not UnitCanAttack("player", self.unit) then
+ ok = false
end
- if br.damaged ~= nil and br.damaged[o.unit] ~= nil then br.damaged[o.unit] = nil end
+ -- Don't break CCs (fast response)
+ if ok and br.functions.misc:getOptionCheck("Don't break CCs") and br.functions.misc:isLongTimeCCed(self.unit) then
+ ok = false
+ end
+ -- No-touch / invuln / affix safety rules
+ if ok and not br.engines.enemiesEngineFunctions:isSafeToAttack(self.unit) then
+ ok = false
+ end
+
+ self.fastInvalidationOk = ok
+ self.fastInvalidationRefresh = now
end
- -- TTD
- if br.getOptionCheck("Enhanced Time to Die") then
- if o.objectID == 140853 then -- If mother, TTD is 10 pct
- o.ttd = o:unitTtd(10)
- elseif o.objectID == 149684 then -- Jaina tps out at 5%
- o.ttd = o:unitTtd(5)
+
+ if self.fastInvalidationOk == false then
+ self.isValidUnit = false
+ br.engines.enemiesEngine.enemy[self.unit] = nil
+ -- Skip expensive validation this pulse; the unit can re-validate on the next pass.
+ br.debug.cpu:updateDebug(startTime, "enemiesEngine.unitSetup.updateUnit")
+ return
+ end
+ end
+
+ -- Is valid unit - only check if enemyList checks out OR if unit is in damaged table
+ if self.enemyListCheck == true then
+ if isDamagedUnit then
+ self.isValidUnit = true
+ else
+ -- Short TTL caching for expensive validation.
+ -- We keep responsiveness for important units and reduce CPU load for stable packs.
+ local now = GetTime()
+ local needsFastValidation = false
+ if br.functions.misc:isTargeting(self.unit) then
+ needsFastValidation = true
+ elseif br.functions.unit:GetUnitIsUnit(self.unit, "target") then
+ needsFastValidation = true
+ elseif br._G.GetNumGroupMembers() > 0 and br.functions.combat:hasThreat(self.unit) then
+ needsFastValidation = true
+ end
+ local validTTL
+ if needsFastValidation then
+ validTTL = 0.05
+ elseif br.engines.enemiesEngine.enemy[self.unit] ~= nil then
+ validTTL = UnitAffectingCombat("player") and 0.15 or 0.25
else
- o.ttd = o:unitTtd()
+ validTTL = UnitAffectingCombat("player") and 0.10 or 0.20
+ end
+
+ if self.validRefresh == nil or self.validRefresh < (now - validTTL) then
+ self.isValidUnit = br.functions.misc:isValidUnit(self.unit)
+ self.validRefresh = now
end
end
- -- Check for loots
- if autoLoot and br.lootable[o.unit] == nil and br.GetUnitIsDeadOrGhost(o.unit) then
- -- print("Checking unit: " .. o.guid .. " for loot.")
- local hasLoot = br._G.CanLootUnit(o.guid)
- if hasLoot then
- -- print("Adding lootable unit")
- o:AddUnit(br.lootable)
+
+ if self.isValidUnit == true then
+ local shouldUpdateDebuffs = br._G.UnitAffectingCombat("player") and
+ (self.distance < 40 or br.functions.unit:GetUnitIsUnit(self.unit, "target"))
+ if shouldUpdateDebuffs then
+ self.debuffs = self:UpdateDebuffs(self.debuffs, self.unit)
+ end
+ if br.engines.enemiesEngine.enemy[self.unit] == nil then
+ self:AddUnit(br.engines.enemiesEngine.enemy)
+ end
+ if shouldUpdateDebuffs then
+ br.engines.enemiesEngine.enemy[self.unit].debuffs = self.debuffs
+ end
+ else
+ if br.engines.enemiesEngine.enemy[self.unit] ~= nil then
+ br.engines.enemiesEngine.enemy[self.unit] = nil
end
end
- -- Add pets
- if br.player ~= nil and br.player.pet.list[o.unit] == nil and (o.objectID == 11492 or br.GetUnitIsUnit(br._G.UnitCreator(o.unit), "player")) then
- o:AddUnit(br.player.pet.list)
+ else
+ self.isValidUnit = false
+ if br.engines.enemiesEngine.enemy[self.unit] ~= nil then
+ br.engines.enemiesEngine.enemy[self.unit] = nil
end
- -- Add other player pets
- if br.pet ~= nil and br.pet[o.unit] == nil and (br._G.UnitIsOtherPlayersPet(o.unit)) then
- o:AddUnit(br.pet)
+ end
+
+ -- TTD
+ if br.functions.misc:getOptionCheck("Enhanced Time to Die") then
+ if self.objectID == 140853 then
+ self.ttd = self:unitTtd(10)
+ elseif self.objectID == 149684 then
+ self.ttd = self:unitTtd(5)
+ else
+ self.ttd = self:unitTtd()
end
+ end
- -- add unit to setup cache
- br.unitSetup.cache[o.unit] = o -- Add unit to SetupTable
- -- Debugging
- br.debug.cpu:updateDebug(startTime, "enemiesEngine.unitSetup.updateUnit")
+ -- Check for loots
+ if autoLoot and br.engines.enemiesEngine.lootable[self.unit] == nil and br.functions.unit:GetUnitIsDeadOrGhost(self.unit) then
+ local hasLoot = br._G.CanLootUnit(self.guid)
+ if hasLoot then
+ self:AddUnit(br.engines.enemiesEngine.lootable)
+ end
+ end
+ -- Add pets
+ if br.player ~= nil and br.player.pet.list[self.unit] == nil and (self.objectID == 11492 or br.functions.unit:GetUnitIsUnit(br._G.UnitCreator(self.unit), "player")) then
+ self:AddUnit(br.player.pet.list)
+ end
+ -- Add other player pets
+ if br.engines.enemiesEngine.pet ~= nil and br.engines.enemiesEngine.pet[self.unit] == nil and (br._G.UnitIsOtherPlayersPet(self.unit)) then
+ self:AddUnit(br.engines.enemiesEngine.pet)
end
- -- Adding the user and functions we just created to this cached version in case we need it again
- -- This will also serve as a good check for if the unit is already in the table easily
- br.unitSetup.cache[o.unit] = o
+ -- add unit to setup cache
+ enemiesEngine.unitSetup.cache[self.unit] = self
-- Debugging
+ br.debug.cpu:updateDebug(startTime, "enemiesEngine.unitSetup.updateUnit")
+ end
+
+ function enemiesEngine.unitSetup:new(unit)
+ local startTime = br._G.debugprofilestop()
+ -- Seeing if we have already cached this unit before
+ if enemiesEngine.unitSetup.cache[unit] then return false end
+ if br.functions.aura:UnitDebuffID("player", 295249) and br._G.UnitIsPlayer(unit) then return false end
+ if enemiesEngine.unitBlacklist[br.functions.unit:GetObjectID(unit)] then return false end
+ local o = {}
+ setmetatable(o, br.engines.enemiesEngine.unitSetup)
+ if unit and (type(unit) == "string" or (br.unlockers.selected == "NN" and type(unit) == "number")) then
+ o.unit = unit
+ end
+ -- Ensure per-unit state is not accidentally shared via the metatable.
+ o.debuffs = {}
+ if o.unit ~= nil then
+ enemiesEngine.unitSetup.cache[o.unit] = o
+ end
br.debug.cpu:updateDebug(startTime, "enemiesEngine.unitSetup")
return o
end
-- Setting up the tables on either Wipe or Initial Setup
- function br.SetupEnemyTables() -- Creating the cache (we use this to check if some1 is already in the table)
+ function enemiesEngine:SetupEnemyTables()
+ -- Creating the cache (we use this to check if some1 is already in the table)
local startTime = br._G.debugprofilestop()
- setmetatable(br.om, br.metaTable2) -- Set the metaTable of Main to Meta
- function br.om:Update()
- br.omTableTimer = GetTime()
+ setmetatable(enemiesEngine.om, enemiesEngine.metaTable2) -- Set the metaTable of Main to Meta
+
+ -- Debugging
+ br.debug.cpu:updateDebug(startTime, "enemiesEngine.enemySetup")
+ end
+
+ function enemiesEngine:Update()
+ enemiesEngine.omTableTimer = GetTime()
--Set variables we don't need to update for each unit
pX, pY, pZ = br._G.ObjectPosition("player")
pCR = br._G.UnitCombatReach("player")
- autoLoot = br.isChecked("Auto Loot")
- if br.pet == nil then
- br.pet = {}
+ autoLoot = br.functions.misc:isChecked("Auto Loot")
+ if br.engines.enemiesEngine.pet == nil then
+ br.engines.enemiesEngine.pet = {}
end
--Make sure we have pet tables
if br.player ~= nil then
@@ -326,68 +479,113 @@ if not br.metaTable2 then
br.player.pet.list = {}
end
end
- --Cycle and clean tables
- local i = 1
- while i <= #br.om do
- if br.om[i].pulseTime == nil or GetTime() >= br.om[i].pulseTime then
- -- this delay is extremely important as the unit updates are a major source of FPS loss for BR
- -- for non-raids, this code will spread out unit updates so that everything gets updated every update
- -- for raids, only half of units will be updated per BR update
- local delay = ((math.random() * 0.25) + 0.75) * br:getUpdateRate()
- if br._G.IsInRaid() then
- delay = delay * 2.00
- end
+ --Cycle and clean tables
+ local i = 1
+ while i <= #enemiesEngine.om do
+ if enemiesEngine.om[i] ~= nil and (enemiesEngine.om[i].pulseTime == nil or GetTime() >= enemiesEngine.om[i].pulseTime) then
+ -- this delay is extremely important as the unit updates are a major source of FPS loss for BR
+ -- for non-raids, this code will spread out unit updates so that everything gets updated every update
+ -- for raids, only half of units will be updated per BR update
+ local delay = ((math.random() * 0.25) + 0.75) * br.engines:getUpdateRate()
+ if br._G.IsInRaid() then
+ delay = delay * 2.00
+ end
- br.om[i].pulseTime = GetTime() + delay
- local thisUnit = br.om[i].unit
+ -- Reduce delay for unvalidated units (not yet in units table) for faster responsiveness
+ -- Once validated and in combat rotation, full throttle applies for FPS optimization
+ if enemiesEngine.om[i].enemyListCheck == nil or br.engines.enemiesEngine.units[enemiesEngine.om[i].unit] == nil then
+ delay = delay * 0.25 -- New/unvalidated units check 4x faster to minimize perceived lag
+ end
+
+ enemiesEngine.om[i].pulseTime = GetTime() + delay
+ local thisUnit = enemiesEngine.om[i].unit
-- setfenv(1, C_Timer.Nn)
- -- br._G.print(tostring(br._G.UnitName(thisUnit)).." is Visible: "..tostring(br.GetUnitIsVisible(thisUnit)))
+ -- br._G.print(tostring(br._G.UnitName(thisUnit)).." is Visible: "..tostring(br.functions.unit:GetUnitIsVisible(thisUnit)))
-- br._G.print(tostring(br._G.UnitName(thisUnit)).." = "..tostring(UnitName(thisUnit)))
- if not br.GetUnitIsVisible(thisUnit) then
+ if not br.functions.unit:GetUnitIsVisible(thisUnit) then
--Delete units no longer in OM
- br.unitSetup.cache[thisUnit] = nil
- if br.ttd[thisUnit] ~= nil then
- br.ttd[thisUnit] = nil
+ enemiesEngine.unitSetup.cache[thisUnit] = nil
+ if enemiesEngine.ttd[thisUnit] ~= nil then
+ enemiesEngine.ttd[thisUnit] = nil
end
- if br.units[thisUnit] ~= nil then
- br.units[thisUnit] = nil
+ if br.engines.enemiesEngine.units[thisUnit] ~= nil then
+ br.engines.enemiesEngine.units[thisUnit] = nil
end
- if br.enemy[thisUnit] ~= nil then
- br.enemy[thisUnit] = nil
+ if br.engines.enemiesEngine.enemy[thisUnit] ~= nil then
+ br.engines.enemiesEngine.enemy[thisUnit] = nil
end
if br.player ~= nil and br.player.pet.list[thisUnit] ~= nil then
br.player.pet.list[thisUnit] = nil
end
- if br.lootable[thisUnit] ~= nil then
- br.lootable[thisUnit] = nil
+ if br.engines.enemiesEngine.lootable[thisUnit] ~= nil then
+ br.engines.enemiesEngine.lootable[thisUnit] = nil
+ end
+ if br.engines.enemiesEngine.pet[thisUnit] ~= nil then
+ br.engines.enemiesEngine.pet[thisUnit] = nil
end
- if br.pet[thisUnit] ~= nil then
- br.pet[thisUnit] = nil
+ -- IMPORTANT: keep enemiesEngine.om as a packed array.
+ -- Setting enemiesEngine.om[i] = nil creates holes, making #enemiesEngine.om unreliable
+ -- and causing this loop (and future tinsert) to miss units.
+ local omIndex = enemiesEngine.omIndex
+ if omIndex ~= nil then
+ omIndex[thisUnit] = nil
end
- tremove(br.om, i)
+ local last = #enemiesEngine.om
+ if i ~= last then
+ enemiesEngine.om[i] = enemiesEngine.om[last]
+ if omIndex ~= nil and enemiesEngine.om[i] and enemiesEngine.om[i].unit ~= nil then
+ omIndex[enemiesEngine.om[i].unit] = i
+ end
+ end
+ enemiesEngine.om[last] = nil
+ -- Do not increment i here; we need to process the swapped-in entry at index i.
else
--Update unit and move to next
- br.om[i]:UpdateUnit()
+ enemiesEngine.om[i]:UpdateUnit()
i = i + 1
end
else
i = i + 1
end
- end
- -- clean our loots
- if autoLoot then
- for k, _ in pairs(br.lootable) do
- if not br._G.CanLootUnit(br.lootable[k].guid) or not br.GetObjectExists(br.lootable[k].unit) then
- br.lootable[k] = nil
+ end
+ -- clean our loots
+ if autoLoot then
+ for k, v in pairs(br.engines.enemiesEngine.lootable) do
+ if v ~= nil and v.unit ~= nil and v.guid ~= nil then
+ -- Check if unit still exists and has loot
+ if not br.functions.unit:GetObjectExists(v.unit) or not br._G.CanLootUnit(v.guid) then
+ br.engines.enemiesEngine.lootable[k] = nil
end
+ else
+ -- Invalid entry, remove it
+ br.engines.enemiesEngine.lootable[k] = nil
end
end
end
- -- Debugging
- br.debug.cpu:updateDebug(startTime, "enemiesEngine.enemySetup")
- end
-
- -- We are setting up the Tables for the first time
- br.SetupEnemyTables()
+ -- Periodic cleanup of unitSetup.cache to avoid stale units inflating enemy counts
+ if enemiesEngine.cacheCleanupTimer == nil then enemiesEngine.cacheCleanupTimer = 0 end
+ local now = GetTime()
+ -- Run cleanup every 5 seconds
+ if (now - enemiesEngine.cacheCleanupTimer) >= 5 then
+ enemiesEngine.cacheCleanupTimer = now
+ for u, entry in pairs(enemiesEngine.unitSetup.cache) do
+ local shouldRemove = false
+ -- If unit no longer exists, dead/ghost or not visible and not referenced elsewhere, remove it
+ if not br.functions.unit:GetObjectExists(u)
+ or br.functions.unit:GetUnitIsDeadOrGhost(u)
+ or not br.functions.unit:GetUnitIsVisible(u)
+ then
+ if (br.engines.enemiesEngine.enemy[u] == nil) and (br.engines.enemiesEngine.units[u] == nil)
+ and (br.engines.enemiesEngine.damaged == nil or br.engines.enemiesEngine.damaged[u] == nil) then
+ shouldRemove = true
+ end
+ end
+ if shouldRemove then
+ enemiesEngine.unitSetup.cache[u] = nil
+ end
+ end
+ end
+ end -- We are setting up the Tables for the first time
+ enemiesEngine:SetupEnemyTables()
end
diff --git a/System/engines/EnemiesEngineFunctions.lua b/System/engines/EnemiesEngineFunctions.lua
index f68509e4a1..68bb0e26a6 100644
--- a/System/engines/EnemiesEngineFunctions.lua
+++ b/System/engines/EnemiesEngineFunctions.lua
@@ -1,14 +1,26 @@
local _, br = ...
local LibDraw = br._G.LibStub("LibDraw-BR")
-br.enemy = {}
-br.lootable = {}
-br.units = {}
-br.objects = {}
-br.storedTables = {}
+br.engines.enemiesEngineFunctions = br.engines.enemiesEngineFunctions or {}
+br.engines.enemiesEngine.enemy = {}
+br.engines.enemiesEngine.lootable = {}
+br.engines.enemiesEngine.units = {}
+br.engines.enemiesEngine.storedTables = {}
+local enemiesEngineFunctions = br.engines.enemiesEngineFunctions
local refreshStored
+local lastOMUpdate = 0
+local lastTrackerUpdate = 0
+-- More reactive update intervals (favor accuracy over FPS)
+local OM_UPDATE_INTERVAL = 0.25 -- Update every 0.25 seconds
+local TRACKER_UPDATE_INTERVAL = 0.05
+local scanOffset = 1 -- Used for batched scanning when object count is high
+
+-- Tracker list reuse (avoid O(n^2) duplicate checks)
+br.engines.tracker = br.engines.tracker or {}
+br.engines.tracker.tracking = br.engines.tracker.tracking or {}
+br.engines.tracker.trackingIndex = br.engines.tracker.trackingIndex or {}
--Check Totem
-function br.isTotem(unit)
+function enemiesEngineFunctions:isTotem(unit)
local creatureType = br._G.UnitCreatureType(unit)
if creatureType ~= nil then
if creatureType == "Totem" or creatureType == "Tótem" or creatureType == "Totém"
@@ -21,91 +33,272 @@ function br.isTotem(unit)
end
local function unitExistsInOM(unit)
- local exists = false
- for index, value in pairs(br.om) do
- if type(value) == "table" and value.unit and value.unit == unit then
- exists = true
- break;
- end
- end
- return exists
+ local omIndex = br.engines.enemiesEngine.omIndex
+ return omIndex ~= nil and omIndex[unit] ~= nil
end
--Update OM
-function br.updateOM()
- local om = br.om
+function enemiesEngineFunctions:updateOM()
+ local now = br._G.GetTime()
local startTime = br._G.debugprofilestop()
- wipe(br.tracking)
+ -- Separate tracker updates from OM updates
+ local misc = br.functions.misc
+ local shouldUpdateTracker = misc:isChecked("Enable Tracker") and (now - lastTrackerUpdate) >= TRACKER_UPDATE_INTERVAL
+ local shouldUpdateOM = (now - lastOMUpdate) >= OM_UPDATE_INTERVAL
+
+ -- Skip building tracker list if no tracker mode is active
+ local trackerModeActive = false
+ if shouldUpdateTracker then
+ local showRare = misc:isChecked("Rare Tracker")
+ local showQuest = misc:isChecked("Quest Tracker")
+ local showCustom = misc:isChecked("Custom Tracker")
+ local customValue = tostring(misc:getOptionValue("Custom Tracker") or "")
+ trackerModeActive = showRare or showQuest or (showCustom and customValue ~= "" and string.len(customValue) >= 3)
+ if not trackerModeActive then
+ shouldUpdateTracker = false
+ end
+ end
+
+ -- Always update tracker list if needed
+ if shouldUpdateTracker then
+ table.wipe(br.engines.tracker.tracking)
+ table.wipe(br.engines.tracker.trackingIndex)
+ lastTrackerUpdate = now
+ end
+
+ local om = br.engines.enemiesEngine.om
+ br.engines.enemiesEngine.omIndex = br.engines.enemiesEngine.omIndex or {}
+ local omIndex = br.engines.enemiesEngine.omIndex
local objUnit
local name
local objectid
local objectguid
- local total = br._G.GetObjectCount(true, "BR") or 0
- for i = 1, total do
- local thisUnit = br._G.GetObjectWithIndex(i)
- -- br._G.print(thisUnit .. " - Is Unit: " .. tostring(br._G.ObjectIsUnit(thisUnit)) .. " - Name: " .. tostring(br._G.UnitName(thisUnit)))
- if br._G.ObjectExists(thisUnit) and br._G.ObjectIsUnit(thisUnit) then
- if not br._G.UnitIsPlayer(thisUnit) and not br.isCritter(thisUnit) and not br._G.UnitIsUnit("player", thisUnit)
- and (not br._G.UnitIsFriend("player", thisUnit) or string.match(br._G.UnitGUID(thisUnit), "Pet"))
+ -- OM is built via index scanning for maximum compatibility across supported unlockers.
+ local totalObjects = br._G.GetObjectCount() or 0
+ local total = math.min(totalObjects, 500)
+
+ -- Localize hot global functions to reduce table lookups in tight loops
+ local GetObjectWithIndex = br._G.GetObjectWithIndex
+ local ObjectExists = br._G.ObjectExists
+ local ObjectIsUnit = br._G.ObjectIsUnit
+ local UnitIsVisible = br._G.UnitIsVisible
+ local UnitIsPlayer = br._G.UnitIsPlayer
+ local UnitIsUnit = br._G.UnitIsUnit
+ local ObjectPosition = br._G.ObjectPosition
+ local UnitName = br._G.UnitName
+ local ObjectName = br._G.ObjectName
+ local ObjectID = br._G.ObjectID
+ local UnitGUID = br._G.UnitGUID
+ local tinsert = br._G.tinsert
+
+ --[[
+ Performance Optimization System:
+
+ 1. FPS-based Range Gating for Tracker:
+ - Dynamically reduces scan radius as FPS drops to maintain smooth performance
+ - Graduated tiers: 30y (< 25 FPS), 50y (< 35 FPS), 70y (< 45 FPS), 100y (< 55 FPS), unlimited (55+ FPS)
+ - Prevents scanning distant objects that won't be interacted with when performance is low
+
+ 2. Object Count Batching:
+ - When > 300 objects exist, limits objects scanned per update cycle
+ - Spreads the load across multiple frames to prevent frame drops
+
+ 3. Distance-based Skipping:
+ - Skips very distant objects for OM updates when FPS is struggling
+ - Works in conjunction with tracker range gating for maximum performance
+ ]]--
+ -- Performance optimization: Dynamically adjust scanning based on object count and FPS
+ -- Cache FPS to avoid expensive GetFramerate() calls multiple times per update
+ local fps = br.engines.enemiesEngine.cachedFPS or br._G.GetFramerate() or 60
+ br.engines.enemiesEngine.cachedFPS = fps -- Cache for use by UpdateUnit
+
+ local objectsPerScan = total
+ local skipDistance = nil
+ local trackerScanRadius = nil -- Range limit for tracker objects
+
+ -- FPS-based range gating for tracker to maintain smooth performance
+ -- Gradually reduce scan radius as FPS drops
+ if fps < 25 then
+ trackerScanRadius = 30 -- Critical FPS: very tight radius
+ skipDistance = 40
+ elseif fps < 35 then
+ trackerScanRadius = 50 -- Low FPS: tight radius
+ skipDistance = 60
+ elseif fps < 45 then
+ trackerScanRadius = 70 -- Medium-low FPS: moderate radius
+ skipDistance = 80
+ elseif fps < 55 then
+ trackerScanRadius = 100 -- Medium FPS: normal radius
+ skipDistance = 100
+ else
+ -- Good FPS (55+): no tracker range limit but still cap OM distance at 100y
+ trackerScanRadius = nil
+ skipDistance = 100 -- Always cap at 100y - nothing beyond this matters for gameplay
+ end
+
+ -- High object count optimization
+ if totalObjects > 300 then
+ -- Limit objects scanned per update based on object density
+ if totalObjects > 450 then
+ objectsPerScan = 150 -- Very high density: scan 1/3 per update
+ elseif totalObjects > 350 then
+ objectsPerScan = 200 -- High density: scan ~half per update
+ else
+ objectsPerScan = 250 -- Moderate-high: scan most per update
+ end
+
+ -- Additionally skip distant objects if performance is struggling
+ -- Override with more aggressive limits at very low FPS
+ if fps < 30 then
+ skipDistance = math.min(skipDistance or 999, 50) -- Extremely aggressive
+ elseif fps < 40 then
+ skipDistance = math.min(skipDistance or 999, 60) -- Only scan within 60 yards
+ elseif fps < 50 then
+ skipDistance = math.min(skipDistance or 999, 80) -- Only scan within 80 yards
+ end
+ end
+
+ -- Always scan for units to add to OM, but only when we should update
+ if shouldUpdateOM then
+ lastOMUpdate = now
+
+ -- Refresh object count each OM update tick (keeps batching accurate).
+ totalObjects = br._G.GetObjectCount() or 0
+ total = math.min(totalObjects, 500)
+
+ -- Calculate scan range for this update
+ local scanStart = scanOffset
+ local scanEnd = math.min(scanOffset + objectsPerScan - 1, total)
+
+ -- Cache player position for distance checks
+ local playerX, playerY, playerZ = ObjectPosition("player")
+
+ for i = scanStart, scanEnd do
+ local thisUnit = GetObjectWithIndex(i)
+ local shouldProcess = false
+
+ if ObjectExists(thisUnit) and ObjectIsUnit(thisUnit) then
+ -- Fast fail checks before expensive operations
+ if not UnitIsPlayer(thisUnit) and not UnitIsUnit("player", thisUnit) then
+ -- OPTIMIZATION: Check visibility FIRST before any other processing
+ -- This is cheap and filters out many units immediately
+ if not UnitIsVisible(thisUnit) then
+ shouldProcess = false
+ else
+ shouldProcess = true
+
+ -- Distance check if we're throttling (with cached position)
+ if skipDistance ~= nil and playerX ~= nil then
+ local unitX, unitY, unitZ = ObjectPosition(thisUnit)
+ if unitX ~= nil then
+ local distance = math.sqrt(((unitX - playerX) ^ 2) + ((unitY - playerY) ^ 2) + ((unitZ - playerZ) ^ 2))
+ if distance > skipDistance then
+ shouldProcess = false -- Skip distant units when performance is low
+ end
+ else
+ shouldProcess = false -- No position = skip
+ end
+ end
+ end
+
+ -- Final validation checks (critter check, etc)
+ -- NOTE: We always allow batched inserts. `skipOMInsert` only reduces scan budget.
+ if shouldProcess and omIndex[thisUnit] == nil and not br.functions.unit:isCritter(thisUnit)
+ --and (not br._G.UnitIsFriend("player", thisUnit) or string.match(br._G.UnitGUID(thisUnit), "Pet"))
+ then
+ local enemyUnit = br.engines.enemiesEngine.unitSetup:new(thisUnit)
+ if enemyUnit then
+ tinsert(om, enemyUnit)
+ omIndex[enemyUnit.unit] = #om
+ end
+ end
+ end
+ end
+
+ -- Update tracker during full OM scan
+ if shouldUpdateTracker
+ and thisUnit ~= nil and ObjectExists(thisUnit)
+ and ((ObjectIsUnit(thisUnit) and UnitIsVisible(thisUnit)) or not ObjectIsUnit(thisUnit))
then
- local enemyUnit = br.unitSetup:new(thisUnit)
- if enemyUnit then
- br._G.tinsert(om, enemyUnit)
+ -- Apply FPS-based range gating for tracker
+ local shouldAddToTracker = true
+ if trackerScanRadius ~= nil and playerX ~= nil then
+ local unitX, unitY, unitZ = ObjectPosition(thisUnit)
+ if unitX ~= nil then
+ local distance = math.sqrt(((unitX - playerX) ^ 2) + ((unitY - playerY) ^ 2) + ((unitZ - playerZ) ^ 2))
+ if distance > trackerScanRadius then
+ shouldAddToTracker = false -- Skip distant units for tracker when FPS is low
+ end
+ end
+ end
+
+ if shouldAddToTracker then
+ objUnit = ObjectIsUnit(thisUnit)
+ name = objUnit and UnitName(thisUnit) or ObjectName(thisUnit)
+ objectid = ObjectID(thisUnit)
+ objectguid = UnitGUID(thisUnit)
+ if thisUnit and name and objectid and objectguid and not br.engines.tracker.trackingIndex[thisUnit] then
+ br.engines.tracker.trackingIndex[thisUnit] = true
+ tinsert(br.engines.tracker.tracking,
+ { object = thisUnit, unit = objUnit, name = name, id = objectid, guid = objectguid })
+ end
end
end
end
- if br.isChecked("Enable Tracker") and thisUnit ~= nil and br._G.ObjectExists(thisUnit)
- and ((br._G.ObjectIsUnit(thisUnit) and br._G.UnitIsVisible(thisUnit) and not br.GetUnitIsDeadOrGhost(thisUnit)) or not br._G.ObjectIsUnit(thisUnit))
- then
- objUnit = br._G.ObjectIsUnit(thisUnit)
- name = objUnit and br._G.UnitName(thisUnit) or br._G.ObjectName(thisUnit)
- objectid = br._G.ObjectID(thisUnit)
- objectguid = br._G.UnitGUID(thisUnit)
- if thisUnit and name and objectid and objectguid then
- local trackerFound = false
- if #br.tracking > 0 then
- for i = 1, #br.tracking do
- local thisTracker = br.tracking[i]
- if thisTracker.object == thisUnit then
- trackerFound = true
- return
+
+ -- Update scan offset for next cycle (batched scanning)
+ scanOffset = scanEnd + 1
+ if scanOffset > total then
+ scanOffset = 1 -- Reset to beginning
+ refreshStored = true
+ end
+ else
+ -- When throttled, only update tracker if needed
+ if shouldUpdateTracker then
+ -- Cache player position for distance checks
+ local playerX, playerY, playerZ = ObjectPosition("player")
+
+ for i = 1, total do
+ local thisUnit = GetObjectWithIndex(i)
+ if ObjectExists(thisUnit)
+ and ((ObjectIsUnit(thisUnit) and UnitIsVisible(thisUnit)) or not ObjectIsUnit(thisUnit))
+ then
+ -- Apply FPS-based range gating for tracker
+ local shouldAddToTracker = true
+ if trackerScanRadius ~= nil and playerX ~= nil then
+ local unitX, unitY, unitZ = ObjectPosition(thisUnit)
+ if unitX ~= nil then
+ local distance = math.sqrt(((unitX - playerX) ^ 2) + ((unitY - playerY) ^ 2) + ((unitZ - playerZ) ^ 2))
+ if distance > trackerScanRadius then
+ shouldAddToTracker = false -- Skip distant units for tracker when FPS is low
+ end
+ end
+ end
+
+ if shouldAddToTracker then
+ objUnit = ObjectIsUnit(thisUnit)
+ name = objUnit and UnitName(thisUnit) or ObjectName(thisUnit)
+ objectid = ObjectID(thisUnit)
+ objectguid = UnitGUID(thisUnit)
+ if thisUnit and name and objectid and objectguid and not br.engines.tracker.trackingIndex[thisUnit] then
+ br.engines.tracker.trackingIndex[thisUnit] = true
+ tinsert(br.engines.tracker.tracking,
+ { object = thisUnit, unit = objUnit, name = name, id = objectid, guid = objectguid })
end
end
- end
- if not trackerFound then
- br._G.tinsert(br.tracking,
- { object = thisUnit, unit = objUnit, name = name, id = objectid, guid = objectguid })
end
end
end
end
- refreshStored = true
-- Debugging
br.debug.cpu:updateDebug(startTime, "enemiesEngine.objects")
-
- -- local counter = 0
- -- local grappleCounter = 0
- -- for i = 1, br._G.GetObjectCount(), 1 do
- -- local guid = br._G.GetObjectWithIndex(i)
- -- if IsGuid(guid) and br._G.ObjectExists(guid) and br._G.ObjectIsUnit(guid) and not unitExistsInOM(guid) and br.omDist(guid) < 50
- -- and not br._G.UnitIsUnit("player", guid) and not br._G.UnitIsFriend("player", guid) and not br._G.UnitIsPlayer(guid)
- -- then
- -- print(ObjectName(guid).." - "..guid)
- -- counter = counter + 1
- -- if ObjectName(guid) == "Grapple Point" then
- -- grappleCounter = grappleCounter + 1
- -- end
- -- end
- -- end
-
- -- br._G.print("OM Count: "..counter..", BR OM Count: "..#br.om..", Grapple Count: "..grappleCounter)
end
-function br.omDist(thisUnit)
+function enemiesEngineFunctions:omDist(thisUnit)
local x1, y1, z1 = br._G.ObjectPosition("player")
local x2, y2, z2 = br._G.ObjectPosition(thisUnit)
if x1 == nil or x2 == nil or y1 == nil or y2 == nil or z1 == nil or z2 == nil then
@@ -116,58 +309,169 @@ function br.omDist(thisUnit)
end
end
-function br.isInOM(thisUnit)
- if #br.om == 0 then return false end
- for i = 1, #br.om do
- local thisX, thisY, thisZ = br._G.ObjectPosition(thisUnit)
- local omX, omY, omZ = br._G.ObjectPosition(br.om[i].guid)
- if --[[br.om[i].guid == thisUnit and]] thisX == omX and thisY == omY and thisZ == omZ then return true end
- end
- return false
+function enemiesEngineFunctions:isInOM(thisUnit)
+ -- Use omIndex for O(1) lookup (previous impl passed .guid to ObjectPosition which is a native crash risk)
+ local omIndex = br.engines.enemiesEngine.omIndex
+ return omIndex ~= nil and omIndex[thisUnit] ~= nil
end
--- /dump br.getEnemies("target",10)
-function br.getEnemies(thisUnit, radius, checkNoCombat, facing)
+-- /dump enemiesEngineFunctions:getEnemies("target",10)
+function enemiesEngineFunctions:getEnemies(thisUnit, radius, checkNoCombat, facing)
local startTime = br._G.debugprofilestop()
radius = tonumber(radius) or 0
- local enemyTable = checkNoCombat and br.units or br.enemy
+ local enemyTable = checkNoCombat and br.engines.enemiesEngine.units or br.engines.enemiesEngine.enemy
local enemiesTable = {}
local thisEnemy, distance
if checkNoCombat == nil then checkNoCombat = false end
if facing == nil then facing = false end
if refreshStored == true then
- for k, _ in pairs(br.storedTables) do br.storedTables[k] = nil end
+ for k, _ in pairs(br.engines.enemiesEngine.storedTables) do br.engines.enemiesEngine.storedTables[k] = nil end
refreshStored = false
end
- if br.storedTables[checkNoCombat] ~= nil then
+ -- if br.engines.enemiesEngine.storedTables[checkNoCombat] ~= nil then
+ -- if checkNoCombat == false then
+ -- if br.engines.enemiesEngine.storedTables[checkNoCombat][thisUnit] ~= nil then
+ -- if br.engines.enemiesEngine.storedTables[checkNoCombat][thisUnit][radius] ~= nil then
+ -- if br.engines.enemiesEngine.storedTables[checkNoCombat][thisUnit][radius][facing] ~= nil then
+ -- --print("Found Table Unit: "..UnitName(thisUnit).." Radius: "..radius.." CombatCheck: "..tostring(checkNoCombat))
+ -- return br.engines.enemiesEngine.storedTables[checkNoCombat][thisUnit][radius][facing]
+ -- end
+ -- end
+ -- end
+ -- end
+ -- end
+ if br.engines.enemiesEngine.storedTables[checkNoCombat] ~= nil then
if checkNoCombat == false then
- if br.storedTables[checkNoCombat][thisUnit] ~= nil then
- if br.storedTables[checkNoCombat][thisUnit][radius] ~= nil then
- if br.storedTables[checkNoCombat][thisUnit][radius][facing] ~= nil then
- --print("Found Table Unit: "..UnitName(thisUnit).." Radius: "..radius.." CombatCheck: "..tostring(checkNoCombat))
- return br.storedTables[checkNoCombat][thisUnit][radius][facing]
+ if br.engines.enemiesEngine.storedTables[checkNoCombat][thisUnit] ~= nil then
+ if br.engines.enemiesEngine.storedTables[checkNoCombat][thisUnit][radius] ~= nil then
+ if br.engines.enemiesEngine.storedTables[checkNoCombat][thisUnit][radius][facing] ~= nil then
+ local cachedTable = br.engines.enemiesEngine.storedTables[checkNoCombat][thisUnit][radius][facing]
+ -- Combat-reactive cache duration: much shorter in combat for higher reactivity
+ local cacheExpiration = br._G.UnitAffectingCombat("player") and 0.02 or 0.05
+ -- Invalidate cache if player moved since cache creation (prevents stale AoE/position info)
+ local px, py, pz = br._G.ObjectPosition("player")
+ local movedTooFar = false
+ if cachedTable._playerPos and px and py and pz then
+ local dx = cachedTable._playerPos.x - px
+ local dy = cachedTable._playerPos.y - py
+ local dz = cachedTable._playerPos.z - pz
+ local moved = math.sqrt((dx * dx) + (dy * dy) + (dz * dz))
+ -- Be aggressive: invalidate cache on small player movement to keep AoE accurate
+ if moved > 0.5 then movedTooFar = true end
+ end
+ if cachedTable._timestamp and (br._G.GetTime() - cachedTable._timestamp) < cacheExpiration and not movedTooFar then
+ return cachedTable
+ end
end
end
end
end
end
- for _, v in pairs(enemyTable) do
- thisEnemy = v.unit
- distance = br.getDistance(thisUnit, thisEnemy)
- if distance < radius and (not facing or br.getFacing("player", thisEnemy)) then
- br._G.tinsert(enemiesTable, thisEnemy)
+ -- Localize frequently used references for performance
+ local rangeFuncs = br.functions.range
+ local unitFuncs = br.functions.unit
+ local tinsert = br._G.tinsert
+
+ -- Dummies should not influence enemy counts/lists unless the player is explicitly
+ -- testing them: only include dummies if they are the player's current target OR
+ -- they are within 8 yards of a targeted dummy.
+ local targetIsDummy = unitFuncs:isDummy("target") and unitFuncs:GetUnitExists("target")
+ local function dummyAllowed(u)
+ if not u or not unitFuncs:isDummy(u) then return true end
+ if unitFuncs:GetUnitIsUnit(u, "target") then return true end
+ if targetIsDummy then
+ local d = rangeFuncs:getDistance(u, "target")
+ if d and d <= 8 then return true end
end
+ return false
end
- if #enemiesTable == 0 and br.getDistance("target", "player") < radius and br.isValidUnit("target") and (not facing or br.getFacing("player", "target")) then
+
+ -- Quick guard: when not requesting `checkNoCombat`, avoid returning enemies
+ -- while the player is not in combat and the requested unit (or player's target)
+ -- isn't a valid unit. This prevents accidental population outside combat.
+ if not checkNoCombat and not br._G.UnitAffectingCombat("player") then
+ local unitToCheck = thisUnit or "player"
+ local validRequest = false
+ if unitToCheck == "player" then
+ -- If caller asked about player-area, require at least a valid target to proceed
+ validRequest = br.functions.misc:isValidUnit("target")
+ else
+ -- For specific units (target, focus, pointers), only proceed if that unit is valid
+ validRequest = br.functions.misc:isValidUnit(unitToCheck)
+ end
+ if not validRequest then
+ if br.functions.misc:isChecked("Enemy List Debug") then
+ br._G.print("[enemiesEngine.getEnemies] Early exit: non-combat and no valid unit for: " .. tostring(unitToCheck))
+ end
+ br.debug.cpu:updateDebug(startTime, "enemiesEngine.getEnemies_quickguard")
+ return {}
+ end
+ end
+
+ -- Fast-path when checking around the player: use precomputed .range where available
+ if thisUnit == "player" then
+ local now = br._G.GetTime()
+ local addedSet = {} -- O(1) hash set replaces O(n) GetUnitIsUnit duplicate scan
+ for _, v in pairs(enemyTable) do
+ local vUnit = v and v.unit
+ if vUnit and not unitFuncs:GetUnitIsDeadOrGhost(vUnit) and dummyAllowed(vUnit) and not addedSet[vUnit] then
+ local d
+ -- Ensure we have an up-to-date range on the enemy entry. Recalculate if missing.
+ if v.range ~= nil and v.timestamp ~= nil and (now - v.timestamp) <= 0.25 then
+ d = v.range
+ else
+ d = rangeFuncs:getDistance("player", vUnit)
+ v.range = d
+ v.timestamp = now
+ end
+ if d and d < radius and (not facing or unitFuncs:getFacing("player", vUnit)) then
+ tinsert(enemiesTable, vUnit)
+ addedSet[vUnit] = true
+ end
+ end
+ end
+ end
+
+ -- FIXED: Improved fallback to include enemies not yet in validated tables
+ -- Check for target if we have one and it's valid
+ if #enemiesTable == 0 and br.functions.unit:GetUnitExists("target")
+ and br.functions.range:getDistance("target", "player") < radius
+ and br.functions.misc:isValidUnit("target")
+ and (not facing or br.functions.unit:getFacing("player", "target")) then
br._G.tinsert(enemiesTable, "target")
end
+
+ -- FIXED: If still empty and no target, check for enemies with threat on player/group (e.g., mobs attacking you)
+ -- This catches enemies that haven't been validated yet but are actively engaging the player
+ if #enemiesTable == 0 and not checkNoCombat and br._G.UnitAffectingCombat("player") then
+ -- Check all units in the broader units table (includes unvalidated units)
+ for _, v in pairs(br.engines.enemiesEngine.units) do
+ local thisEnemy = v.unit
+ if dummyAllowed(thisEnemy)
+ and br.functions.unit:GetUnitExists(thisEnemy)
+ and not br.functions.unit:GetUnitIsDeadOrGhost(thisEnemy) then
+ local distance = br.functions.range:getDistance(thisUnit, thisEnemy)
+ if distance < radius and (not facing or br.functions.unit:getFacing("player", thisEnemy)) then
+ -- Check if this enemy has threat on us or is targeting us
+ if br.functions.combat:hasThreat(thisEnemy) or br.functions.misc:isTargeting(thisEnemy) then
+ br._G.tinsert(enemiesTable, thisEnemy)
+ end
+ end
+ end
+ end
+ end
---
if #enemiesTable > 0 and thisUnit ~= nil then
- if br.storedTables[checkNoCombat] == nil then br.storedTables[checkNoCombat] = {} end
- if br.storedTables[checkNoCombat][thisUnit] == nil then br.storedTables[checkNoCombat][thisUnit] = {} end
- if br.storedTables[checkNoCombat][thisUnit][radius] == nil then br.storedTables[checkNoCombat][thisUnit][radius] = {} end
- br.storedTables[checkNoCombat][thisUnit][radius][facing] = enemiesTable
+ if br.engines.enemiesEngine.storedTables[checkNoCombat] == nil then br.engines.enemiesEngine.storedTables[checkNoCombat] = {} end
+ if br.engines.enemiesEngine.storedTables[checkNoCombat][thisUnit] == nil then br.engines.enemiesEngine.storedTables[checkNoCombat][thisUnit] = {} end
+ if br.engines.enemiesEngine.storedTables[checkNoCombat][thisUnit][radius] == nil then br.engines.enemiesEngine.storedTables[checkNoCombat][thisUnit][radius] = {} end
+ -- FIXED: Add timestamp to cached table so cache expiration check works properly
+ local px, py, pz = br._G.ObjectPosition("player")
+ enemiesTable._timestamp = br._G.GetTime()
+ -- store player position at cache time to detect movement-based invalidation
+ enemiesTable._playerPos = { x = px, y = py, z = pz }
+ br.engines.enemiesEngine.storedTables[checkNoCombat][thisUnit][radius][facing] = enemiesTable
--print("Made Table Unit: "..UnitName(thisUnit).." Radius: "..radius.." CombatCheck: "..tostring(checkNoCombat))
end
-- Debugging
@@ -176,12 +480,12 @@ function br.getEnemies(thisUnit, radius, checkNoCombat, facing)
end
-- function to see if our unit is a blacklisted unit
-function br.isBlackListed(Unit)
+function enemiesEngineFunctions:isBlackListed(Unit)
-- check if unit is valid
- if br.GetObjectExists(Unit) then
+ if br.functions.unit:GetObjectExists(Unit) then
for i = 1, #br.castersBlackList do
-- check if unit is valid
- if br.GetObjectExists(br.castersBlackList[i].unit) then
+ if br.functions.unit:GetObjectExists(br.castersBlackList[i].unit) then
if br.castersBlackList[i].unit == Unit then
return true
end
@@ -191,16 +495,16 @@ function br.isBlackListed(Unit)
end
-- returns true if target should be burnt
-function br.isBurnTarget(unit)
+function enemiesEngineFunctions:isBurnTarget(unit)
local coef = 0
-- check if unit is valid
- if br.getOptionCheck("Forced Burn") and br._G.UnitExists(unit) and br.hasThreat(unit) then
- local unitID = br.GetObjectID(unit)
+ if br.functions.misc:getOptionCheck("Forced Burn") and br._G.UnitExists(unit) and br.functions.combat:hasThreat(unit) then
+ local unitID = br.functions.unit:GetObjectID(unit)
local burnUnit = br.lists.burnUnits[unitID]
- local unitTime = br.units[unit] ~= nil and br.units[unit].timestamp or br._G.GetTime() - 1
+ local unitTime = br.engines.enemiesEngine.units[unit] ~= nil and br.engines.enemiesEngine.units[unit].timestamp or br._G.GetTime() - 1
-- if unit have selected debuff
- if burnUnit and burnUnit.isValidUnit and (burnUnit.cast == nil or not br.isCasting(burnUnit.cast, unitID)) and (br._G.GetTime() - unitTime) > 0.25 then
- if burnUnit.buff and br.UnitBuffID(unit, burnUnit.buff) then
+ if burnUnit and burnUnit.isValidUnit and (burnUnit.cast == nil or not br.functions.cast:isCasting(burnUnit.cast, unitID)) and (br._G.GetTime() - unitTime) > 0.25 then
+ if burnUnit.buff and br.functions.aura:UnitBuffID(unit, burnUnit.buff) then
coef = burnUnit.coef
end
if not burnUnit.buff and (br._G.UnitName(unit) == burnUnit.name or --[[burnUnit or]] burnUnit.id == unitID) then
@@ -213,11 +517,11 @@ function br.isBurnTarget(unit)
end
-- check for a unit see if its a cc candidate
-function br.isCrowdControlCandidates(Unit)
+function enemiesEngineFunctions:isCrowdControlCandidates(Unit)
local unitID
-- check if unit is valid
- if br.GetObjectExists(Unit) then
- unitID = br.GetObjectID(Unit)
+ if br.functions.unit:GetObjectExists(Unit) then
+ unitID = br.functions.unit:GetObjectID(Unit)
else
return false
end
@@ -227,7 +531,7 @@ function br.isCrowdControlCandidates(Unit)
for i = 1, #crowdControlUnit do
local thisUnit = crowdControlUnit[i]
-- is in the list of candidates
- if thisUnit.spell == nil or br.isCasting(thisUnit.spell, Unit) or br.UnitBuffID(thisUnit.buff) then -- doesnt have more requirements or requirements are met
+ if thisUnit.spell == nil or br.functions.cast:isCasting(thisUnit.spell, Unit) or br.functions.aura:UnitBuffID(thisUnit.buff) then -- doesnt have more requirements or requirements are met
return true
end
end
@@ -236,29 +540,29 @@ function br.isCrowdControlCandidates(Unit)
end
-- returns true if we can safely attack this target
-function br.isSafeToAttack(unit)
- if br.getOptionCheck("Safe Damage Check") == true then
+function enemiesEngineFunctions:isSafeToAttack(unit)
+ if br.functions.misc:getOptionCheck("Safe Damage Check") == true then
local startTime = br._G.debugprofilestop()
-- check if unit is valid
- local unitID = br.GetObjectExists(unit) and br.GetObjectID(unit) or 0
+ local unitID = br.functions.unit:GetObjectExists(unit) and br.functions.unit:GetObjectID(unit) or 0
for i = 1, #br.lists.noTouchUnits do
local noTouch = br.lists.noTouchUnits[i]
if noTouch.unitID == 1 or noTouch.unitID == unitID then
if noTouch.buff == nil then return false end --If a unit exist in the list without a buff it's just blacklisted
if noTouch.buff > 0 then
- local unitTTD = br.getTTD(unit) or 0
+ local unitTTD = br.engines.ttdTable:getTTD(unit) or 0
-- Not Safe with Buff/Debuff
- if br.UnitBuffID(unit, noTouch.buff) or br.UnitDebuffID(unit, noTouch.buff)
+ if br.functions.aura:UnitBuffID(unit, noTouch.buff) or br.functions.aura:UnitDebuffID(unit, noTouch.buff)
-- Bursting M+ Affix
- or (unitTTD <= br.getDebuffRemain("player", 240443) + (br.getGlobalCD(true) * 2)
- and br.getDebuffStacks("player", 240443) >= br.getOptionValue("Bursting Stack Limit"))
+ or (unitTTD <= br.functions.aura:getDebuffRemain("player", 240443) + (br.functions.spell:getGlobalCD(true) * 2)
+ and br.functions.aura:getDebuffStacks("player", 240443) >= br.functions.misc:getOptionValue("Bursting Stack Limit"))
then
return false
end
else
-- Not Safe without Buff/Debuff
local posBuff = -(noTouch.buff)
- if not br.UnitBuffID(unit, posBuff) or not br.UnitDebuffID(unit, posBuff) then
+ if not br.functions.aura:UnitBuffID(unit, posBuff) or not br.functions.aura:UnitDebuffID(unit, posBuff) then
return false
end
end
@@ -274,14 +578,14 @@ end
-- returns true if target is shielded or should be avoided
local function isShieldedTarget(unit)
local coef = 0
- if br.getOptionCheck("Avoid Shields") then
+ if br.functions.misc:getOptionCheck("Avoid Shields") then
-- check if unit is valid
- local unitID = br.GetObjectID(unit)
+ local unitID = br.functions.unit:GetObjectID(unit)
local shieldedUnit = br.lists.shieldUnits[unitID]
-- if unit have selected debuff
- if shieldedUnit and shieldedUnit.buff and br.UnitBuffID(unit, shieldedUnit.buff) then
+ if shieldedUnit and shieldedUnit.buff and br.functions.aura:UnitBuffID(unit, shieldedUnit.buff) then
-- if it's a frontal buff, see if we are in front of it
- if shieldedUnit.frontal ~= true or br.getFacing(unit, "player") then
+ if shieldedUnit.frontal ~= true or br.functions.unit:getFacing(unit, "player") then
coef = shieldedUnit.coef
end
end
@@ -289,27 +593,49 @@ local function isShieldedTarget(unit)
return coef
end
+local coefficientCache = {}
+-- Make coefficient cache shorter to adapt to fast-changing combat situations
+local COEF_CACHE_TIME = 0.1
+local lastCoefficientCleanup = 0
-- This function will set the prioritisation of the units, ie which target should i attack
local function getUnitCoeficient(unit)
local startTime = br._G.debugprofilestop()
- local coef = 0
- -- if distance == nil then distance = br.getDistance("player",unit) end
- local distance = br.getDistance("player", unit)
+ local now = br._G.GetTime()
+
+ -- Periodic cleanup of dead units from cache (once per second)
+ if now - lastCoefficientCleanup > 1 then
+ for cachedUnit in pairs(coefficientCache) do
+ if not br.functions.unit:GetObjectExists(cachedUnit) then
+ coefficientCache[cachedUnit] = nil
+ end
+ end
+ lastCoefficientCleanup = now
+ end
+
+ local cached = coefficientCache[unit]
+ if cached and (now - cached.time) < COEF_CACHE_TIME then
+ return cached.value
+ end
+
+ local startTime = br._G.debugprofilestop()
+ local coef = 0
+ -- if distance == nil then distance = br.functions.range:getDistance("player",unit) end
+ local distance = br.functions.range:getDistance("player", unit)
-- check if unit is valid
- if br.GetObjectExists(unit) then
+ if br.functions.unit:GetObjectExists(unit) then
-- if unit is out of range, bad prio(0)
if distance < 50 then
- local unitHP = br.getHP(unit)
+ local unitHP = br.functions.unit:getHP(unit)
-- if wise target checked, we look for best target by looking to the lowest or highest hp, otherwise we look for target
- if br.getOptionCheck("Wise Target") == true then
- if br.getOptionValue("Wise Target") == 1 then -- Highest
+ if br.functions.misc:getOptionCheck("Wise Target") == true then
+ if br.functions.misc:getOptionValue("Wise Target") == 1 then -- Highest
-- if highest is selected
coef = unitHP
- elseif br.getOptionValue("Wise Target") == 3 then -- abs Highest
+ elseif br.functions.misc:getOptionValue("Wise Target") == 3 then -- abs Highest
coef = br._G.UnitHealth(unit)
- elseif br.getOptionValue("Wise Target") == 5 then -- Nearest
+ elseif br.functions.misc:getOptionValue("Wise Target") == 5 then -- Nearest
coef = 100 - distance
- elseif br.getOptionValue("Wise Target") == 6 then -- Furthest
+ elseif br.functions.misc:getOptionValue("Wise Target") == 6 then -- Furthest
coef = distance
else -- Lowest
-- if lowest is selected
@@ -318,49 +644,49 @@ local function getUnitCoeficient(unit)
end
-- Distance Coef add for multiple burn units (Will prioritize closest first)
coef = coef + ((50 - distance) / 100)
- -- if its our actual target we give it a bonus
- if br.GetUnitIsUnit("target", unit) == true and not br.GetUnitIsDeadOrGhost(unit) then
- coef = coef + 50
+ -- if its our actual target we give it a bonus (increased to strongly prefer selected target)
+ if br.functions.unit:GetUnitIsUnit("target", unit) == true and not br.functions.unit:GetUnitIsDeadOrGhost(unit) then
+ coef = coef + 200
end
-- raid target management
-- if the unit have the skull and we have param for it add 50
- if br.getOptionCheck("Skull First") and br._G.GetRaidTargetIndex(unit) == 8 then
+ if br.functions.misc:getOptionCheck("Skull First") and br._G.GetRaidTargetIndex(unit) == 8 then
coef = coef + 50
end
-- if threat is checked, add 100 points of prio if we lost aggro on that target
- if br.getOptionCheck("Tank Threat") then
+ if br.functions.misc:getOptionCheck("Tank Threat") then
local threat = br._G.UnitThreatSituation("player", unit) or -1
- if select(6, br._G.GetSpecializationInfo(br._G.GetSpecialization())) == "TANK" and threat < 3 and unitHP > 10 then
+ if select(6, br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization())) == "TANK" and threat < 3 and unitHP > 10 then
coef = coef + 100 - threat
end
end
- if br.isChecked("Prioritize Totems") and br.isTotem(unit) then
+ if br.functions.misc:isChecked("Prioritize Totems") and enemiesEngineFunctions:isTotem(unit) then
coef = coef + 100
end
-- if user checked burn target then we add the value otherwise will be 0
- if br.getOptionCheck("Forced Burn") then
- coef = coef + br.isBurnTarget(unit) + ((50 - distance) / 100)
+ if br.functions.misc:getOptionCheck("Forced Burn") then
+ coef = coef + enemiesEngineFunctions:isBurnTarget(unit) + ((50 - distance) / 100)
end
-- if user checked avoid shielded, we add the % this shield remove to coef
- if br.getOptionCheck("Avoid Shields") then
+ if br.functions.misc:getOptionCheck("Avoid Shields") then
coef = coef + isShieldedTarget(unit)
end
-- Outlaw - Blind Shot 10% dmg increase all sources
- if select(2, br._G.UnitClass('player')) == "ROGUE" and br._G.GetSpecializationInfo(br._G.GetSpecialization()) == 260 then
+ if select(2, br._G.UnitClass('player')) == "ROGUE" and br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization()) == 260 then
-- Between the eyes
- if br.getDebuffRemain(unit, 315341) > 0 then
+ if br.functions.aura:getDebuffRemain(unit, 315341) > 0 then
coef = coef + 75
end
-- Blood of the enemy
- if br.getDebuffRemain(unit, 297108) > 0 then
+ if br.functions.aura:getDebuffRemain(unit, 297108) > 0 then
coef = coef + 50
end
-- Marked for death
- if br.getDebuffRemain(unit, 137619) > 0 then
+ if br.functions.aura:getDebuffRemain(unit, 137619) > 0 then
coef = coef + 75
end
-- Prey on the weak
- if br.getDebuffRemain(unit, 131511) > 0 then
+ if br.functions.aura:getDebuffRemain(unit, 131511) > 0 then
coef = coef + 50
end
end
@@ -369,6 +695,16 @@ local function getUnitCoeficient(unit)
-- Print("Unit "..displayName.." - "..displayCoef)
end
end
+
+ -- Allow profiles to add custom coefficient weights via units.customTargetWeight
+ if br.player and br.player.units and br.player.units.customTargetWeight then
+ local customWeight = br.player.units.customTargetWeight(unit)
+ if customWeight and type(customWeight) == "number" then
+ coef = coef + customWeight
+ end
+ end
+
+ coefficientCache[unit] = { value = coef, time = now }
-- Debugging
br.debug.cpu:updateDebug(startTime, "enemiesEngine.unitCoef")
return coef
@@ -376,20 +712,268 @@ end
local function compare(a, b)
if br._G.UnitHealth(a) == br._G.UnitHealth(b) then
- return br.getDistance(a) < br.getDistance(b)
+ return br.functions.range:getDistance(a) < br.functions.range:getDistance(b)
else
return br._G.UnitHealth(a) < br._G.UnitHealth(b)
end
end
+-- local bestUnitCache = {}
+-- -- Shorten best unit cache to improve target re-evaluation frequency
+-- local BEST_UNIT_CACHE_TIME = 0.12
+-- -- Finds the "best" unit for a given range and optional facing
+-- local function findBestUnit(range, facing)
+-- local tsort = table.sort
+-- local startTime = br._G.debugprofilestop()
+-- local bestUnitCoef
+-- local bestUnit = nil
+-- local now = br._G.GetTime()
+-- local cacheKey = range .. "_" .. tostring(facing)
+-- local cached = bestUnitCache[cacheKey]
+
+-- -- Validate cached unit still exists before returning
+-- if cached and (now - cached.time) < BEST_UNIT_CACHE_TIME then
+-- if cached.unit and br.functions.unit:GetObjectExists(cached.unit)
+-- and not br.functions.unit:GetUnitIsDeadOrGhost(cached.unit) then
+-- -- Don't use cached dummies - they shouldn't be auto-selected
+-- if br.functions.unit:isDummy(cached.unit) then
+-- bestUnitCache[cacheKey] = nil
+-- -- Don't return, fall through to recalculate
+-- else
+-- -- Additional validation for cached unit
+-- local stillValid = true
+-- if br.functions.misc:getOptionCheck("Safe Damage Check") and not enemiesEngineFunctions:isSafeToAttack(cached.unit) then
+-- stillValid = false
+-- end
+-- if stillValid then
+-- return cached.unit
+-- end
+-- end
+-- end
+-- -- Cached unit invalid, clear cache entry
+-- bestUnitCache[cacheKey] = nil
+-- end
+
+-- local enemyList = enemiesEngineFunctions:getEnemies("player", range, false, facing)
+
+-- -- Early exit if no enemies
+-- if #enemyList == 0 then
+-- bestUnitCache[cacheKey] = { unit = nil, time = now }
+-- br.debug.cpu:updateDebug(startTime, "enemiesEngine.findBestUnit")
+-- return nil
+-- end
+
+-- -- Cache option checks to avoid repeated function calls (moved before single-target optimization)
+-- local dontBreakCC = br.functions.misc:getOptionCheck("Don't break CCs")
+-- local safeDamageCheck = br.functions.misc:getOptionCheck("Safe Damage Check")
+-- local wiseTargetEnabled = br.functions.misc:getOptionCheck("Wise Target")
+-- local wiseTargetMode = wiseTargetEnabled and br.functions.misc:getOptionValue("Wise Target") == 4
+-- local avoidShields = br.functions.misc:getOptionCheck("Avoid Shields")
+
+-- -- Single target optimization - skip coefficient calculation
+-- if #enemyList == 1 then
+-- local onlyUnit = enemyList[1]
+-- -- Still need to validate single enemy isn't a dummy/unsafe
+-- -- Dummies should not be auto-selected by dynamic targeting
+-- if br.functions.unit:isDummy(onlyUnit) then
+-- bestUnitCache[cacheKey] = { unit = nil, time = now }
+-- br.debug.cpu:updateDebug(startTime, "enemiesEngine.findBestUnit")
+-- return nil
+-- end
+-- if safeDamageCheck and not enemiesEngineFunctions:isSafeToAttack(onlyUnit) then
+-- bestUnitCache[cacheKey] = { unit = nil, time = now }
+-- br.debug.cpu:updateDebug(startTime, "enemiesEngine.findBestUnit")
+-- return nil
+-- end
+-- bestUnitCache[cacheKey] = { unit = onlyUnit, time = now }
+-- br.debug.cpu:updateDebug(startTime, "enemiesEngine.findBestUnit")
+-- return onlyUnit
+-- end
+
+-- -- Limit processing to prevent long loops
+-- local maxProcess = math.min(#enemyList, 20)
+-- if #enemyList > maxProcess then
+-- -- Only process subset for performance
+-- local subset = {}
+-- for i = 1, maxProcess do
+-- subset[i] = enemyList[i]
+-- end
+-- enemyList = subset
+-- end
+
+-- -- Single sort operation (remove duplicate sort)
+-- tsort(enemyList, compare)
+
+-- local currHP
+-- for i = 1, #enemyList do
+-- local thisUnit = enemyList[i]
+-- local unitID = br.functions.unit:GetObjectExists(thisUnit) and br.functions.unit:GetObjectID(thisUnit) or 0
+
+-- -- Special case for specific boss mechanics
+-- if ((unitID == 135360 or unitID == 135358 or unitID == 135359) and br.functions.aura:UnitBuffID(thisUnit, 260805))
+-- or (unitID ~= 135360 and unitID ~= 135358 and unitID ~= 135359) then
+
+-- -- Skip dummies from dynamic targeting - they should not be auto-selected
+-- if br.functions.unit:isDummy(thisUnit) then
+-- -- Skip this unit
+-- else
+-- -- Expensive checks - only if option enabled
+-- local isCC = dontBreakCC and #enemyList > 1 and br.functions.misc:isLongTimeCCed(thisUnit) or false
+-- local isSafe = (safeDamageCheck and enemiesEngineFunctions:isSafeToAttack(thisUnit)) or not safeDamageCheck or false
+
+-- if not isCC and isSafe then
+-- local coeficient = getUnitCoeficient(thisUnit) or 0
+
+-- -- Wise target absolute lowest HP mode
+-- if wiseTargetMode then
+-- if currHP == nil or br._G.UnitHealth(thisUnit) < currHP then
+-- currHP = br._G.UnitHealth(thisUnit)
+-- coeficient = coeficient + 100
+-- end
+-- end
+
+-- if coeficient >= 0 and (bestUnitCoef == nil or coeficient > bestUnitCoef) then
+-- bestUnitCoef = coeficient
+-- bestUnit = thisUnit
+-- end
+-- end
+-- end -- Close the else block for dummy/shield check
+-- end
+-- end
+
+-- bestUnitCache[cacheKey] = { unit = bestUnit, time = now }
+-- -- Debugging
+-- br.debug.cpu:updateDebug(startTime, "enemiesEngine.findBestUnit")
+-- return bestUnit
+-- end
+
+-- -- Sets Target by attempting to find the best unit else defaults to target
+-- function enemiesEngineFunctions:dynamicTarget(range, facing)
+-- if range == nil or range > 100 then return nil end
+-- local startTime = br._G.debugprofilestop()
+-- facing = facing or false
+-- local bestUnit = nil
+-- local tarDist = br.functions.unit:GetObjectExists("target") and br.functions.range:getDistance("target") or 99
+-- local bestDist
+-- local inCombat = br._G.UnitAffectingCombat("player")
+-- local autoTargetDynamic = br.functions.misc:isChecked("Target Dynamic Target")
+
+-- -- Determine if the current target is valid for our purposes.
+-- local targetExists = br.functions.unit:GetUnitExists("target")
+-- local targetFacingOK = (not facing) or br.functions.unit:getFacing("player", "target")
+-- local targetInRange = targetExists and tarDist < range
+-- local targetValid = targetExists
+-- and not br.functions.unit:GetUnitIsDeadOrGhost("target")
+-- and targetInRange
+-- and targetFacingOK
+-- and br.functions.misc:isValidUnit("target")
+-- local notSafe = br.functions.misc:getOptionCheck("Safe Damage Check") and not enemiesEngineFunctions:isSafeToAttack("target") or false
+-- if targetValid and notSafe then
+-- targetValid = false
+-- end
+
+-- -- Stick to a valid current target unless the user explicitly wants to follow the dynamic target.
+-- if targetValid and not autoTargetDynamic then
+-- bestUnit = "target"
+-- else
+
+-- if br.functions.misc:isChecked("Dynamic Targetting") then
+-- -- Wise Target Frequency: "Only on Target Death" mode (option value 2)
+-- if br.functions.misc:getOptionValue("Wise Target Frequency") == 2 then
+-- -- Stick with current target if it exists, is valid, alive, and in range
+-- if br.functions.unit:GetObjectExists("target")
+-- and not br.functions.unit:GetUnitIsDeadOrGhost("target")
+-- and tarDist < range
+-- and (not facing or br.functions.unit:getFacing("player", "target"))
+-- and br.functions.misc:isValidUnit("target")
+-- then
+-- bestUnit = "target"
+-- else
+-- -- Only recalculate when target is dead/invalid
+-- bestUnit = findBestUnit(range, facing)
+-- end
+-- -- Default mode: recalculate based on combat/setting
+-- elseif br.functions.misc:getOptionValue("Dynamic Targetting") == 2
+-- or (br._G.UnitAffectingCombat("player") and br.functions.misc:getOptionValue("Dynamic Targetting") == 1)
+-- and (bestUnit == nil or (br.functions.unit:GetUnitIsUnit(bestUnit, "target") and tarDist >= range))
+-- then
+-- bestUnit = findBestUnit(range, facing)
+-- end
+-- end
+
+-- if (not br.functions.misc:isChecked("Dynamic Targetting") or bestUnit == nil)
+-- and (tarDist < range or br.functions.unit:GetUnitExists("target"))
+-- and (not facing or (facing and br.functions.unit:getFacing("player", "target"))) and br.functions.misc:isValidUnit("target")
+-- then
+-- bestUnit = "target"
+-- end
+
+-- -- If we still don't have a unit and we're in combat and need one (invalid target OR forcing dynamic), find one.
+-- if bestUnit == nil and inCombat and (autoTargetDynamic or not targetValid) then
+-- bestUnit = findBestUnit(range, facing)
+-- end
+-- end
+
+-- -- If we have a candidate bestUnit, validate it is actually within range.
+-- if bestUnit ~= nil and bestUnit ~= "target" then
+-- bestDist = br.functions.range:getDistance(bestUnit)
+-- if bestDist == nil or bestDist >= range then
+-- bestUnit = nil
+-- elseif not br.functions.misc:isValidUnit(bestUnit) then
+-- bestUnit = nil
+-- end
+-- end
+
+-- -- Optional: keep the WoW target in sync with the dynamic target.
+-- -- Targeting rules:
+-- -- 1) If "Target Dynamic Target" is enabled: always target bestUnit (when one exists).
+-- -- 2) If disabled: only target bestUnit when our current target is no longer valid.
+-- if inCombat then
+-- -- Decide whether we should retarget.
+-- local wantRetarget = autoTargetDynamic or (not br.functions.misc:isValidUnit(bestUnit)) or notSafe
+
+-- -- Only do anything if we have a valid dynamic target candidate and it's not already our target.
+-- if wantRetarget
+-- and bestUnit ~= nil
+-- and br.functions.unit:GetObjectExists(bestUnit)
+-- and not br.functions.unit:GetUnitIsUnit(bestUnit, "target")
+-- then
+-- local shouldTarget = true
+-- -- -- Hard gate: don't auto-target units that aren't currently valid/engaged.
+-- -- if shouldTarget and not br.functions.misc:isValidUnit(bestUnit) then
+-- -- shouldTarget = false
+-- -- end
+-- -- Never auto-target dummies.
+-- if br.functions.unit:isDummy(bestUnit) then
+-- shouldTarget = false
+-- end
+-- -- -- Respect Safe Damage Check for the chosen unit.
+-- -- if shouldTarget and br.functions.misc:getOptionCheck("Safe Damage Check") and not enemiesEngineFunctions:isSafeToAttack(bestUnit) then
+-- -- shouldTarget = false
+-- -- end
+-- -- Optional: don't auto-target explosives unless facing is not required (keeps previous intent).
+-- if shouldTarget and (not facing) and br.functions.unit:isExplosive(bestUnit) then
+-- shouldTarget = false
+-- end
+-- if shouldTarget then
+-- print("Dynamic Targeting: Switching target to " .. (br.functions.unit:GetUnitName(bestUnit) or "unknown"))
+-- br._G.TargetUnit(bestUnit)
+-- end
+-- end
+-- end
+-- -- Debugging
+-- br.debug.cpu:updateDebug(startTime, "enemiesEngine.dynamicTarget")
+-- return bestUnit
+-- end
+
-- Finds the "best" unit for a given range and optional facing
local function findBestUnit(range, facing)
local tsort = table.sort
local startTime = br._G.debugprofilestop()
local bestUnitCoef
local bestUnit = nil
- local enemyList = br.getEnemies("player", range, false, facing)
- if bestUnit ~= nil and br.enemy[bestUnit] == nil then bestUnit = nil end
+ local enemyList = enemiesEngineFunctions:getEnemies("player", range, false, facing)
+ if bestUnit ~= nil and br.engines.enemiesEngine.enemy[bestUnit] == nil then bestUnit = nil end
if bestUnit == nil
-- or GetTime() > lastCheckTime
then
@@ -399,20 +983,21 @@ local function findBestUnit(range, facing)
tsort(enemyList, compare)
for i = 1, #enemyList do
local thisUnit = enemyList[i]
- local unitID = br.GetObjectExists(thisUnit) and br.GetObjectID(thisUnit) or 0
- if ((unitID == 135360 or unitID == 135358 or unitID == 135359) and br.UnitBuffID(thisUnit, 260805)) or (unitID ~= 135360 and unitID ~= 135358 and unitID ~= 135359) then
- local isCC = br.getOptionCheck("Don't break CCs") and #enemyList > 1 and br.isLongTimeCCed(thisUnit) or
- false
- local isSafe = (br.getOptionCheck("Safe Damage Check") and br.isSafeToAttack(thisUnit)) or
- not br.getOptionCheck("Safe Damage Check") or false
+ local unitID = br.functions.unit:GetObjectExists(thisUnit) and br.functions.unit:GetObjectID(thisUnit) or 0
+ if ((unitID == 135360 or unitID == 135358 or unitID == 135359)
+ and br.functions.aura:UnitBuffID(thisUnit, 260805)) or (unitID ~= 135360 and unitID ~= 135358 and unitID ~= 135359)
+ then
+ local isCC = br.functions.misc:getOptionCheck("Don't break CCs") and #enemyList > 1 and br.functions.misc:isLongTimeCCed(thisUnit) or false
+ local isSafe = (br.functions.misc:getOptionCheck("Safe Damage Check") and enemiesEngineFunctions:isSafeToAttack(thisUnit)) or
+ not br.functions.misc:getOptionCheck("Safe Damage Check") or false
-- local thisUnit = v.unit
-- local distance = br.getDistance(thisUnit)
-- if distance < range then
if not isCC and isSafe then
local coeficient = getUnitCoeficient(thisUnit) or 0
- if br.getOptionCheck("Wise Target") == true and br.getOptionValue("Wise Target") == 4 then -- abs Lowest
- if currHP == nil or br._G.UnitHealth(thisUnit) < currHP then
- currHP = br._G.UnitHealth(thisUnit)
+ if br.functions.misc:getOptionCheck("Wise Target") == true and br.functions.misc:getOptionValue("Wise Target") == 4 then -- abs Lowest
+ if currHP == nil or br.functions.unit:UnitHealth(thisUnit) < currHP then
+ currHP = br.functions.unit:UnitHealth(thisUnit)
coeficient = coeficient + 100
end
end
@@ -432,32 +1017,32 @@ local function findBestUnit(range, facing)
end
-- Sets Target by attempting to find the best unit else defaults to target
-function br.dynamicTarget(range, facing)
+function enemiesEngineFunctions:dynamicTarget(range, facing)
if range == nil or range > 100 then return nil end
local startTime = br._G.debugprofilestop()
facing = facing or false
local bestUnit = nil
- local tarDist = br.GetObjectExists("target") and br.getDistance("target") or 99
+ local tarDist = br.functions.unit:GetObjectExists("target") and br.functions.range:getDistance("target") or 99
local bestDist
- if br.isChecked("Dynamic Targetting") then
- if br.getOptionValue("Dynamic Targetting") == 2 or (br._G.UnitAffectingCombat("player") and br.getOptionValue("Dynamic Targetting") == 1)
- and (bestUnit == nil or (br.GetUnitIsUnit(bestUnit, "target") and tarDist >= range))
+ if br.functions.misc:isChecked("Dynamic Targetting") then
+ if br.functions.misc:getOptionValue("Dynamic Targetting") == 2 or (br.functions.combat:UnitAffectingCombat("player") and br.functions.misc:getOptionValue("Dynamic Targetting") == 1)
+ and (bestUnit == nil or (br.functions.unit:GetUnitIsUnit(bestUnit, "target") and tarDist >= range))
then
bestUnit = findBestUnit(range, facing)
end
end
- if (not br.isChecked("Dynamic Targetting") or bestUnit == nil) and tarDist < range
- and (not facing or (facing and br.getFacing("player", "target"))) and br.isValidUnit("target")
+ if (not br.functions.misc:isChecked("Dynamic Targetting") or bestUnit == nil) --and tarDist < range
+ and (not facing or (facing and br.functions.unit:getFacing("player", "target"))) and br.functions.misc:isValidUnit("target")
then
bestUnit = "target"
end
- bestDist = br.getDistance(bestUnit) or 99
- if bestDist < range and not br.GetUnitIsUnit(bestUnit, "target") then
- if ((br.GetUnitIsDeadOrGhost("target") and not br.GetUnitIsFriend("target", "player")) or (not br.GetUnitExists("target") and br.hasThreat(bestUnit))
- or ((br.isChecked("Target Dynamic Target") and br.GetUnitExists("target"))))
- or (br.getOptionCheck("Forced Burn") and br.isBurnTarget(bestUnit) > 0 and br.GetUnitExists(bestUnit)
- and ((not facing and not br.isExplosive(bestUnit)) or (facing and br.getFacing("player", bestUnit))))
- or (br.getOptionCheck("Safe Damage Check") and not br.isSafeToAttack("target"))
+ bestDist = br.functions.range:getDistance(bestUnit) or 99
+ if bestDist < range and not br.functions.unit:GetUnitIsUnit(bestUnit, "target") then
+ if ((br.functions.unit:GetUnitIsDeadOrGhost("target") and not br.functions.unit:GetUnitIsFriend("target", "player")) or (not br.functions.unit:GetObjectExists("target") and br.functions.combat:hasThreat(bestUnit))
+ or ((br.functions.misc:isChecked("Target Dynamic Target") and br.functions.unit:GetObjectExists("target"))))
+ or (br.functions.misc:getOptionCheck("Forced Burn") and enemiesEngineFunctions:isBurnTarget(bestUnit) > 0 and br.functions.unit:GetObjectExists(bestUnit)
+ and ((not facing and not br.functions.unit:isExplosive(bestUnit)) or (facing and br.functions.unit:getFacing("player", bestUnit))))
+ or (br.functions.misc:getOptionCheck("Safe Damage Check") and not enemiesEngineFunctions:isSafeToAttack("target"))
then
if bestUnit ~= nil then
br._G.TargetUnit(bestUnit)
@@ -470,10 +1055,10 @@ function br.dynamicTarget(range, facing)
end
local function angleDifference(unit1, unit2)
- local facing = br.GetObjectFacing(unit1)
- local distance = br.getDistance(unit1, unit2)
- local unit1X, unit1Y, unit1Z = br.GetObjectPosition(unit1)
- local unit2X, unit2Y, _ = br.GetObjectPosition(unit2)
+ local facing = br.functions.unit:GetObjectFacing(unit1)
+ local distance = br.functions.range:getDistance(unit1, unit2)
+ local unit1X, unit1Y, unit1Z = br.functions.unit:GetObjectPosition(unit1)
+ local unit2X, unit2Y, _ = br.functions.unit:GetObjectPosition(unit2)
local pX, pY, _ = br._G.GetPositionFromPosition(unit1X, unit1Y, unit1Z, distance, facing, 0)
local vectorAX, vectorAY = unit1X - pX, unit1Y - pY
local vectorBX, vectorBY = unit1X - unit2X, unit1Y - unit2Y
@@ -496,30 +1081,33 @@ end
-- Cone Logic for Enemies
local coneUnits = {}
-function br.getEnemiesInCone(angle, length, checkNoCombat, showLines)
+function enemiesEngineFunctions:getEnemiesInCone(angle, length, checkNoCombat, showLines)
if angle == nil then angle = 180 end
if length == nil then length = 0 end
- local playerX, playerY, playerZ = br.GetObjectPosition("player")
- local facing = br.GetObjectFacing("player")
+ local playerX, playerY, playerZ = br.functions.unit:GetObjectPosition("player")
+ local facing = br.functions.unit:GetObjectFacing("player")
local unitsCounter = 0
- local enemiesTable = br.getEnemies("player", length, checkNoCombat, true)
+ local enemiesTable = enemiesEngineFunctions:getEnemies("player", length, checkNoCombat, true)
local inside = false
---@diagnostic disable-next-line: undefined-field
if showLines then LibDraw.Arc(playerX, playerY, playerZ, length, angle, 0) end
table.wipe(coneUnits)
- for i = 1, #enemiesTable do
+ -- OPTIMIZATION: Limit processing to first 30 enemies for performance
+ -- In massive AOE situations, processing every unit is unnecessary and crushes FPS
+ local maxProcess = math.min(#enemiesTable, 30)
+ for i = 1, maxProcess do
local thisUnit = enemiesTable[i]
local radius = br._G.UnitCombatReach(thisUnit)
local unitX, unitY, unitZ = br._G.GetPositionBetweenObjects(thisUnit, "player", radius)
if playerX and unitX and playerY and unitY then
- for j = radius, 0, -0.1 do
+ for j = radius, 0, -0.5 do
inside = false
if j > 0 then
unitX, unitY = br._G.GetPositionBetweenObjects(thisUnit, "player", j)
else
- unitX, unitY = br.GetObjectPosition(thisUnit)
+ unitX, unitY = br.functions.unit:GetObjectPosition(thisUnit)
end
- local angleToUnit = br.getAngles(playerX, playerY, playerZ, unitX, unitY, unitZ)
+ local angleToUnit = br.engines.healingEngineFunctions:getAngles(playerX, playerY, playerZ, unitX, unitY, unitZ)
local angleDifference = facing > angleToUnit and facing - angleToUnit or angleToUnit - facing
local shortestAngle = angleDifference < math.pi and angleDifference or math.pi * 2 - angleDifference
local finalAngle = shortestAngle / math.pi * 180
@@ -539,17 +1127,17 @@ function br.getEnemiesInCone(angle, length, checkNoCombat, showLines)
end
end
- -- br.ChatOverlay(units)
+ -- br.ui.chatOverlay:Show(units)
return unitsCounter, coneUnits
end
-- Rectangle Logic for Enemies
local rectUnits = {}
-function br.getEnemiesInRect(width, length, showLines, checkNoCombat)
- local px, py, pz = br.GetObjectPosition("player")
+function enemiesEngineFunctions:getEnemiesInRect(width, length, showLines, checkNoCombat)
+ local px, py, pz = br.functions.unit:GetObjectPosition("player")
if px == nil or py == nil or pz == nil then return {} end
local function getRect(width, length)
- local facing = br.GetObjectFacing("player") or 0
+ local facing = br.functions.unit:GetObjectFacing("player") or 0
local halfWidth = width / 2
-- Near Left
local nlX, nlY, _ = br._G.GetPositionFromPosition(px, py, pz, halfWidth, facing + math.rad(90), 0)
@@ -565,11 +1153,14 @@ function br.getEnemiesInRect(width, length, showLines, checkNoCombat)
checkNoCombat = checkNoCombat or false
local nlX, nlY, nrX, nrY, frX, frY = getRect(width, length)
local enemyCounter = 0
- local enemiesTable = br.getEnemies("player", length, checkNoCombat, true)
+ local enemiesTable = enemiesEngineFunctions:getEnemies("player", length, checkNoCombat, true)
local inside = false
if #enemiesTable > 0 then
- _G.table.wipe(rectUnits)
- for i = 1, #enemiesTable do
+ _G.table.wipe(rectUnits)
+ -- OPTIMIZATION: Limit processing to first 40 enemies for performance
+ -- Rectangle checks are expensive with position calculations
+ local maxProcess = math.min(#enemiesTable, 40)
+ for i = 1, maxProcess do
local thisUnit = enemiesTable[i]
local radius = br._G.UnitCombatReach(thisUnit)
local tX, tY = br._G.GetPositionBetweenObjects(thisUnit, "player", radius)
@@ -580,9 +1171,9 @@ function br.getEnemiesInRect(width, length, showLines, checkNoCombat)
if j > 0 then
pX, pY = br._G.GetPositionBetweenObjects(thisUnit, "player", j)
else
- pX, pY = br.GetObjectPosition(thisUnit)
+ pX, pY = br.functions.unit:GetObjectPosition(thisUnit)
end
- if br.isInside(pX, pY, nlX, nlY, nrX, nrY, frX, frY) then
+ if br.engines.healingEngineFunctions:isInside(pX, pY, nlX, nlY, nrX, nrY, frX, frY) then
inside = true
break
end
@@ -631,15 +1222,15 @@ end
-- end
-- Percentage of enemies that are not in execute HP range
-function br.getNonExecuteEnemiesPercent(executeHP)
+function enemiesEngineFunctions:getNonExecuteEnemiesPercent(executeHP)
local executeCount = 0
local nonexecuteCount = 0
local nonexecutePercent = 0
- for k, _ in pairs(br.enemy) do
- local thisUnit = br.enemy[k]
- if br.GetObjectExists(thisUnit.unit) then
- if br.getHP(thisUnit) < executeHP then
+ for k, _ in pairs(br.engines.enemiesEngine.enemy) do
+ local thisUnit = br.engines.enemiesEngine.enemy[k]
+ if br.functions.unit:GetObjectExists(thisUnit.unit) then
+ if br.functions.unit:getHP(thisUnit) < executeHP then
executeCount = executeCount + 1
else
nonexecuteCount = nonexecuteCount + 1
@@ -654,9 +1245,9 @@ function br.getNonExecuteEnemiesPercent(executeHP)
end
-- Tracks AoE Damage
-function br.AoEDamageTracker()
+function enemiesEngineFunctions:AoEDamageTracker()
for i = 1, #br.lists.AoEDamage do
- if br.DBM:getTimer(br.lists.AoEDamage[i]) ~= 999 then
+ if br.functions.DBM:getTimer(br.lists.AoEDamage[i]) ~= 999 then
br.curAoESpell = br.lists.AoEDamage[i]
if br.lastAoESpell == nil then
br.lastAoESpell = br.curAoESpell
@@ -668,7 +1259,7 @@ function br.AoEDamageTracker()
br.lastAoESpell = br.curAoESpell
br.burstCount = 1
end
- return br.DBM:getTimer(br.lists.AoEDamage[i]), br.burstCount
+ return br.functions.DBM:getTimer(br.lists.AoEDamage[i]), br.burstCount
end
end
return -1
diff --git a/System/engines/HealingEngine.lua b/System/engines/HealingEngine.lua
index 0e850875e8..724870a7cd 100644
--- a/System/engines/HealingEngine.lua
+++ b/System/engines/HealingEngine.lua
@@ -1,40 +1,42 @@
local _, br = ...
+br.engines.healingEngine = br.engines.healingEngine or {} -- This will be the MetaTable attached to our Main Table that the world will see
+local healingEngine = br.engines.healingEngine
+
--[[---------------------------------------------------------------------------------------------------
-----------------------------------------Bubba's Healing Engine--------------------------------------]]
-local LGIST = br._G.LibStub("LibGroupInSpecT-1.1")
-if not br.metaTable1 then
+-- local LGIST = br._G.LibStub("LibGroupInSpecT-1.1")
+if not healingEngine.metaTable1 then
-- localizing the commonly used functions while inside loops
local getDistance, tinsert, tremove, UnitClass, GetUnitIsUnit = br.getDistance, br._G.tinsert, br._G.tremove, br._G.UnitClass, br.GetUnitIsUnit
local UnitHealth, UnitHealthMax = br._G.UnitHealth, br._G.UnitHealthMax
local GetTime, UnitDebuffID = br._G.GetTime, br.UnitDebuffID
- br.friend = {} -- This is our main Table that the world will see
- br.memberSetup = {} -- This is one of our MetaTables that will be the default user/contructor
- br.memberSetup.cache = {} -- This is for the cache Table to check against
- br.metaTable1 = {} -- This will be the MetaTable attached to our Main Table that the world will see
- br.metaTable1.__call = function(_) -- (_, forceRetable, excludePets, onlyInRange) [Not Implemented]
+ br.engines.healingEngine.friend = {} -- This is our main Table that the world will see
+ healingEngine.memberSetup = {} -- This is one of our MetaTables that will be the default user/contructor
+ healingEngine.memberSetup.cache = {} -- This is for the cache Table to check against
+ healingEngine.__call = function(_) -- (_, forceRetable, excludePets, onlyInRange) [Not Implemented]
local group = br._G.IsInRaid() and "raid" or "party" -- Determining if the UnitID will be raid or party based
local groupSize = br._G.IsInRaid() and br._G.GetNumGroupMembers() or br._G.GetNumGroupMembers() - 1 -- If in raid, we check the entire raid. If in party, we remove one from max to account for the player.
if group == "party" then
- tinsert(br.friend, br.memberSetup:new("player"))
+ tinsert(br.engines.healingEngine.friend, healingEngine.memberSetup:new("player"))
end -- We are creating a new User for player if in a Group
for i = 1, groupSize do -- start of the loop to read throught the party/raid
local groupUnit = group .. i
- local groupMember = br.memberSetup:new(groupUnit)
+ local groupMember = healingEngine.memberSetup:new(groupUnit)
if groupMember then
- tinsert(br.friend, groupMember)
+ tinsert(br.engines.healingEngine.friend, groupMember)
end -- Inserting a newly created Unit into the Main Frame
end
end
- br.metaTable1.__index = {
+ healingEngine.__index = {
-- Setting the Metamethod of Index for our Main Table
name = "Healing Table",
author = "Bubba"
}
-- Creating a default Unit to default to on a check
- br.memberSetup.__index = {
+ healingEngine.memberSetup.__index = {
name = "noob",
hp = 100,
unit = "noob",
@@ -50,15 +52,30 @@ if not br.metaTable1 then
updateHealingTable:SetScript(
"OnEvent",
function()
- _G.table.wipe(br.friend)
- _G.table.wipe(br.memberSetup.cache)
- br.SetupTables()
+ -- Track group size changes for buff stability
+ healingEngine.lastGroupSize = healingEngine.currentGroupSize or 0
+ healingEngine.currentGroupSize = #br.engines.healingEngine.friend
+ healingEngine.groupSizeChangeTime = br._G.GetTime()
+
+ _G.table.wipe(br.engines.healingEngine.friend)
+ _G.table.wipe(healingEngine.memberSetup.cache)
+ healingEngine:SetupTables()
end
)
+
+ -- Helper function to check if group composition is stable (for buff logic)
+ -- Returns true if group size hasn't changed in the last 3 seconds
+ function healingEngine:isGroupStable(seconds)
+ seconds = seconds or 3 -- Default to 3 seconds
+ if not healingEngine.groupSizeChangeTime then
+ return true -- No changes recorded yet, assume stable
+ end
+ return (br._G.GetTime() - healingEngine.groupSizeChangeTime) >= seconds
+ end
-- This is for those NPC units that need healing. Compare them against our list of Unit ID's
local function SpecialHealUnit(tar)
- for i = 1, #br.novaEngineTables.SpecialHealUnitList do
- if br.getGUID(tar) == br.novaEngineTables.SpecialHealUnitList[i] then
+ for i = 1, #br.engines.healingEngineCollections.SpecialHealUnitList do
+ if br.functions.unit:getGUID(tar) == br.engines.healingEngineCollections.SpecialHealUnitList[i] then
return true
end
end
@@ -69,7 +86,7 @@ if not br.metaTable1 then
local auraInfo = br._G.UnitAura(tar, i, "HELPFUL|HARMFUL")
if auraInfo then
if auraInfo.name then
- if br.novaEngineTables.BadDebuffList[auraInfo.spellID] ~= nil then
+ if br.engines.healingEngineCollections.BadDebuffList[auraInfo.spellID] ~= nil then
return false
end
end
@@ -80,9 +97,9 @@ if not br.metaTable1 then
-- This is for NPC units we do not want to heal. Compare to list in collections.
local function CheckSkipNPC(tar)
- local npcId = (br.getGUID(tar))
- for i = 1, #br.novaEngineTables.skipNPC do
- if npcId == br.novaEngineTables.skipNPC[i] then
+ local npcId = (br.functions.unit:getGUID(tar))
+ for i = 1, #br.engines.healingEngineCollections.skipNPC do
+ if npcId == br.engines.healingEngineCollections.skipNPC[i] then
return true
end
end
@@ -102,33 +119,33 @@ if not br.metaTable1 then
end
end
-- Verifying the target is a Valid Healing target
- function br.HealCheck(tar)
+ function healingEngine:HealCheck(tar)
if
- (br.GetUnitIsVisible(tar) and br.GetUnitReaction("player", tar) > 4 and not br.GetUnitIsDeadOrGhost(tar) and br.getLineOfSight("player", tar) and CheckCreatureType(tar) and
- not br._G.UnitPhaseReason(tar)) and
- ((br.isPlayer(tar) and br._G.UnitIsConnected(tar) and CheckBadDebuff(tar) and not br._G.UnitIsCharmed(tar) and not br._G.UnitIsOtherPlayersPet(tar)) or
- ((GetUnitIsUnit(tar,"pet") or br._G.UnitIsOtherPlayersPet(tar)) and br.getOptionCheck("Heal Pets")) or
- br.novaEngineTables.SpecialHealUnitList[tonumber(select(2, br.getGUID(tar)))] ~= nil)
+ (br.functions.unit:GetUnitIsVisible(tar) and br.functions.unit:GetUnitReaction("player", tar) > 4 and not br.functions.unit:GetUnitIsDeadOrGhost(tar) and br.functions.misc:getLineOfSight("player", tar) and CheckCreatureType(tar) and
+ br._G.UnitInPhase(tar)) and
+ ((br.functions.misc:isPlayer(tar) and br._G.UnitIsConnected(tar) and CheckBadDebuff(tar) and not br._G.UnitIsCharmed(tar) and not br._G.UnitIsOtherPlayersPet(tar)) or
+ ((GetUnitIsUnit(tar,"pet") or br._G.UnitIsOtherPlayersPet(tar)) and br.functions.misc:getOptionCheck("Heal Pets")) or
+ br.engines.healingEngineCollections.SpecialHealUnitList[tonumber(select(2, br.functions.unit:getGUID(tar)))] ~= nil)
then
return true
else
return false
end
end
- function br.memberSetup:new(unit)
+ function healingEngine.memberSetup:new(unit)
-- Seeing if we have already cached this unit before
- if br.memberSetup.cache[br.getGUID(unit)] then
+ if healingEngine.memberSetup.cache[br.functions.unit:getGUID(unit)] then
return false
end
local o = {}
- setmetatable(o, br.memberSetup)
+ setmetatable(o, healingEngine.memberSetup)
if unit and type(unit) == "string" then
o.unit = unit
end
-- We are checking the HP of the person through their own function.
function o:CalcHP()
- local toxicBrand = br.getOptionValue("Toxic Brand")
- local necroRot = br.getOptionValue("Necrotic Rot")
+ local toxicBrand = br.functions.misc:getOptionValue("Toxic Brand")
+ local necroRot = br.functions.misc:getOptionValue("Necrotic Rot")
if toxicBrand == 0 or toxicBrand == nil then
toxicBrand = 10
end
@@ -139,23 +156,23 @@ if not br.metaTable1 then
if o.unit == nil then return 0, 0, 0 end
-- Darkness phase of Kil'Jaeden. basically blacklists all friends if I have this debuff, since I can't heal.
-- but once I have Illidan's Sightless Gaze (241721), I can hea
- if br.isChecked("Necrotic Rot") and br.getDebuffStacks(o.unit, 209858) > 0 and br.getDebuffStacks(o.unit, 209858) >= necroRot then
+ if br.functions.misc:isChecked("Necrotic Rot") and br.functions.aura:getDebuffStacks(o.unit, 209858) > 0 and br.functions.aura:getDebuffStacks(o.unit, 209858) >= necroRot then
return 250, 250, 250
end
- if br.UnitBuffID(o.unit, 360618) then
+ if br.functions.aura:UnitBuffID(o.unit, 360618) then
return 250, 250, 250
end
- if br.UnitBuffID(o.unit, 295271) then
+ if br.functions.aura:UnitBuffID(o.unit, 295271) then
return 250, 250, 250
end
- if br.UnitBuffID(o.unit, 344916) then
+ if br.functions.aura:UnitBuffID(o.unit, 344916) then
return 250, 250, 250
end
- if br.UnitBuffID(o.unit, 327140) or br.UnitBuffID(o.unit, 327676) then
+ if br.functions.aura:UnitBuffID(o.unit, 327140) or br.functions.aura:UnitBuffID(o.unit, 327676) then
return 250, 250, 250
end
- if br.isChecked("Toxic Brand") and br.player.eID and br.player.eID == 2298 then
- if br.getDebuffStacks(o.unit, 294715) > 0 and br.getDebuffStacks(o.unit, 294715) >= toxicBrand then
+ if br.functions.misc:isChecked("Toxic Brand") and br.player.eID and br.player.eID == 2298 then
+ if br.functions.aura:getDebuffStacks(o.unit, 294715) > 0 and br.functions.aura:getDebuffStacks(o.unit, 294715) >= toxicBrand then
return 250, 250, 250
end
end
@@ -163,11 +180,11 @@ if not br.metaTable1 then
return 250, 250, 250
end
if br.player.eID == 2331 then
- local time_remain = br.DBM:getPulltimer(nil, 313213)
- if time_remain < br.getOptionValue("Decaying Strike Timer") then
+ local time_remain = br.functions.DBM:getPulltimer(nil, 313213)
+ if time_remain < br.functions.misc:getOptionValue("Decaying Strike Timer") then
for i = 1, br._G.GetObjectCount() do
local thisUnit = br._G.GetObjectWithIndex(i)
- if br.GetObjectID(thisUnit) == 156866 and br._G.UnitTarget(thisUnit) ~= nil then
+ if br.functions.unit:GetObjectID(thisUnit) == 156866 and br._G.UnitTarget(thisUnit) ~= nil then
if br._G.UnitTarget(thisUnit) == o.unit then
return 250, 250, 250
end
@@ -208,7 +225,7 @@ if not br.metaTable1 then
-- Place Dead players at the end of the list
if o.hcRefresh == nil or o.hcRefresh < GetTime() - 1 then
local startTime = br._G.debugprofilestop()
- if br.HealCheck(o.unit) ~= true or UnitHealth(o.unit) == 0 then
+ if healingEngine:HealCheck(o.unit) ~= true or UnitHealth(o.unit) == 0 then
return 250, 250, 250
end
br.debug.cpu.healingEngine.HealCheck = br._G.debugprofilestop() - startTime
@@ -216,38 +233,38 @@ if not br.metaTable1 then
end
-- incoming heals
local incomingheals
- if br.getOptionCheck("Incoming Heals") == true and br._G.UnitGetIncomingHeals(o.unit, "player") ~= nil then
+ if br.functions.misc:getOptionCheck("Incoming Heals") == true and br._G.UnitGetIncomingHeals(o.unit, "player") ~= nil then
incomingheals = br._G.UnitGetIncomingHeals(o.unit, "player")
else
incomingheals = 0
end
-- absorbs
local nAbsorbs
- if br.getOptionCheck("Ignore Absorbs") ~= true then
+ if br.functions.misc:getOptionCheck("Ignore Absorbs") ~= true then
nAbsorbs = (br._G.UnitGetTotalAbsorbs(o.unit) * 0.25)
else
nAbsorbs = 0
end
-- calc base + absorbs + incomings
local PercentWithIncoming = 100 * (UnitHealth(o.unit) + incomingheals + nAbsorbs) / UnitHealthMax(o.unit)
- if br.getOptionCheck("Prioritize Tank") then
+ if br.functions.misc:getOptionCheck("Prioritize Tank") then
-- Using the group role assigned to the Unit
if o.role == "TANK" then
- PercentWithIncoming = PercentWithIncoming - br.getOptionValue("Prioritize Tank")
+ PercentWithIncoming = PercentWithIncoming - br.functions.misc:getOptionValue("Prioritize Tank")
end
-- Tank in Proving Grounds
if o.guidsh == 72218 then
- PercentWithIncoming = PercentWithIncoming - br.getOptionValue("Prioritize Tank")
+ PercentWithIncoming = PercentWithIncoming - br.functions.misc:getOptionValue("Prioritize Tank")
end
-- Using threat to remove %hp from all tanking units
if o.threat == 3 then
- PercentWithIncoming = PercentWithIncoming - br.getOptionValue("Prioritize Tank")
+ PercentWithIncoming = PercentWithIncoming - br.functions.misc:getOptionValue("Prioritize Tank")
end
end
- -- if br.getOptionCheck("Prioritize Debuff") then
+ -- if br.functions.misc:getOptionCheck("Prioritize Debuff") then
-- -- Using Dispel Check to see if we should give bonus weight
-- if o.dispel then
- -- PercentWithIncoming = PercentWithIncoming - br.getOptionValue("Prioritize Debuff")
+ -- PercentWithIncoming = PercentWithIncoming - br.functions.misc:getOptionValue("Prioritize Debuff")
-- end
-- end
local ActualWithIncoming = (UnitHealthMax(o.unit) - (UnitHealth(o.unit) + incomingheals))
@@ -255,7 +272,7 @@ if not br.metaTable1 then
local debugTimerStartTime = GetTime()
for i = 1, 40 do
local _, _, count, _, _, _, _, _, _, SpellID = br._G.UnitAura(o.unit, i, "HELPFUL|HARMFUL")
- local debuffID = br.novaEngineTables.SpecificHPDebuffs[SpellID]
+ local debuffID = br.engines.healingEngineCollections.SpecificHPDebuffs[SpellID]
if debuffID ~= nil then
for i = 1, #debuffID do
local debuffData = debuffID[i]
@@ -270,10 +287,10 @@ if not br.metaTable1 then
if elapsedDebugTime > 0.5 then
br._G.print("WARNING: Debuff Scan took a long time: " .. elapsedDebugTime .. " Seconds")
end
- if br.getOptionCheck("Blacklist") == true and br.data.blackList ~= nil then
+ if br.functions.misc:getOptionCheck("Blacklist") == true and br.data.blackList ~= nil then
for i = 1, #br.data.blackList do
if o.guid == br.data.blackList[i].guid then
- PercentWithIncoming, ActualWithIncoming, nAbsorbs = PercentWithIncoming + br.getValue("Blacklist"), ActualWithIncoming + br.getValue("Blacklist"), nAbsorbs + br.getValue("Blacklist")
+ PercentWithIncoming, ActualWithIncoming, nAbsorbs = PercentWithIncoming + br.functions.misc:getValue("Blacklist"), ActualWithIncoming + br.functions.misc:getValue("Blacklist"), nAbsorbs + br.functions.misc:getValue("Blacklist")
break
end
end
@@ -284,7 +301,7 @@ if not br.metaTable1 then
function o:nGUID()
local nShortHand = ""
local targetGUID = ""
- if br.GetUnitIsVisible(unit) then
+ if br.functions.unit:GetUnitIsVisible(unit) then
targetGUID = br._G.UnitGUID(unit)
nShortHand = br._G.UnitGUID(unit):sub(-5)
end
@@ -296,8 +313,8 @@ if not br.metaTable1 then
if GetUnitIsUnit("player", o.unit) then
return UnitClass("player")
end
- if br.novaEngineTables.roleTable[br._G.UnitName(o.unit)] ~= nil then
- return br.novaEngineTables.roleTable[br._G.UnitName(o.unit)].class
+ if br.engines.healingEngineCollections.roleTable[br._G.UnitName(o.unit)] ~= nil then
+ return br.engines.healingEngineCollections.roleTable[br._G.UnitName(o.unit)].class
end
else
return UnitClass(o.unit)
@@ -307,14 +324,15 @@ if not br.metaTable1 then
function o:GetRole()
if br._G.UnitGroupRolesAssigned(o.unit) == "NONE" then
-- if we already have a role defined we dont scan
- if br.novaEngineTables.roleTable[br._G.UnitName(o.unit)] ~= nil then
- return br.novaEngineTables.roleTable[br._G.UnitName(o.unit)].role
+ if br.engines.healingEngineCollections.roleTable[br._G.UnitName(o.unit)] ~= nil then
+ return br.engines.healingEngineCollections.roleTable[br._G.UnitName(o.unit)].role
else
---@diagnostic disable-next-line: undefined-field
- local info = LGIST:GetCachedInfo(br.getGUID(o.unit))
- if info and info.spec_role then
- return info.spec_role
- end
+ -- local info = LGIST:GetCachedInfo(br.functions.unit:getGUID(o.unit))
+ -- if info and info.spec_role then
+ -- return info.spec_role
+ -- end
+ return ""
end
else
return br._G.UnitGroupRolesAssigned(o.unit)
@@ -322,9 +340,9 @@ if not br.metaTable1 then
end
-- sets actual position of unit in engine, shouldnt refresh more than once/sec
function o:GetPosition()
- if br.GetUnitIsVisible(o.unit) and br.getLineOfSight("player",o.unit) then
+ if br.functions.unit:GetUnitIsVisible(o.unit) and br.functions.misc:getLineOfSight("player",o.unit) then
o.refresh = GetTime()
- local x, y, z = br.GetObjectPosition(o.unit)
+ local x, y, z = br.functions.unit:GetObjectPosition(o.unit)
x = math.ceil(x * 100) / 100
y = math.ceil(y * 100) / 100
z = math.ceil(z * 100) / 100
@@ -350,7 +368,7 @@ if not br.metaTable1 then
end
-- Updating the values of the Unit
function o:UpdateUnit()
- if br.isChecked("Debug Timers") then
+ if br.functions.misc:isChecked("Debug Timers") then
local startTime
local debugprofilestop = br._G.debugprofilestop
@@ -461,22 +479,23 @@ if not br.metaTable1 then
o.x, o.y, o.z = o:GetPosition()
end
-- add unit to setup cache
- br.memberSetup.cache[select(2, br.getGUID(o.unit))] = o -- Add unit to SetupTable
+ healingEngine.memberSetup.cache[select(2, br.functions.unit:getGUID(o.unit))] = o -- Add unit to SetupTable
end
-- Adding the user and functions we just created to this cached version in case we need it again
-- This will also serve as a good check for if the unit is already in the table easily
- --Print(UnitName(unit), select(2, br.getGUID(unit)))
- br.memberSetup.cache[select(2, o:nGUID())] = o
+ --Print(UnitName(unit), select(2, br.functions.unit:getGUID(unit)))
+ healingEngine.memberSetup.cache[select(2, o:nGUID())] = o
return o
end
-- Setting up the tables on either Wipe or Initial Setup
- function br.SetupTables() -- Creating the cache (we use this to check if some1 is already in the table)
- setmetatable(br.friend, br.metaTable1) -- Set the metaTable of Main to Meta
- function br.friend:Update()
+ function healingEngine:SetupTables()
+ -- Creating the cache (we use this to check if some1 is already in the table)
+ setmetatable(br.engines.healingEngine.friend, healingEngine) -- Set the metaTable of Main to Meta
+ function br.engines.healingEngine.friend:Update()
-- Print("HEAL PULSE: "..GetTime()) -- debug Print to check update time
-- This is for special situations, IE world healing or NPC healing in encounters
- local selectedMode, SpecialTargets = br.getOptionValue("Special Heal"), {}
- if br.getOptionCheck("Special Heal") == true then
+ local selectedMode, SpecialTargets = br.functions.misc:getOptionValue("Special Heal"), {}
+ if br.functions.misc:getOptionCheck("Special Heal") == true then
if selectedMode == 1 then
SpecialTargets = {"target"}
elseif selectedMode == 2 then
@@ -489,63 +508,63 @@ if not br.metaTable1 then
end
for p = 1, #SpecialTargets do
-- Checking if Unit Exists and it's possible to heal them
- if br.GetUnitExists(SpecialTargets[p]) and br.HealCheck(SpecialTargets[p]) and not CheckSkipNPC(SpecialTargets[p]) then
- if not br.memberSetup.cache[select(2, br.getGUID(SpecialTargets[p]))] then
- local SpecialCase = br.memberSetup:new(SpecialTargets[p])
+ if br.functions.unit:GetUnitExists(SpecialTargets[p]) and healingEngine:HealCheck(SpecialTargets[p]) and not CheckSkipNPC(SpecialTargets[p]) then
+ if not healingEngine.memberSetup.cache[select(2, br.functions.unit:getGUID(SpecialTargets[p]))] then
+ local SpecialCase = healingEngine.memberSetup:new(SpecialTargets[p])
if SpecialCase then
-- Creating a new user, if not already tabled, will return with the User
- for j = 1, #br.friend do
- if br.friend[j].unit == SpecialTargets[p] then
+ for j = 1, #br.engines.healingEngine.friend do
+ if br.engines.healingEngine.friend[j].unit == SpecialTargets[p] then
-- Now we add the Unit we just created to the Main Table
- for k, _ in pairs(br.memberSetup.cache) do
- if br.friend[j].guidsh == k then
- br.memberSetup.cache[k] = nil
+ for k, _ in pairs(healingEngine.memberSetup.cache) do
+ if br.engines.healingEngine.friend[j].guidsh == k then
+ healingEngine.memberSetup.cache[k] = nil
end
end
- tremove(br.friend, j)
+ tremove(br.engines.healingEngine.friend, j)
break
end
end
end
- tinsert(br.friend, SpecialCase)
- br.novaEngineTables.SavedSpecialTargets[SpecialTargets[p]] = select(2, br.getGUID(SpecialTargets[p]))
+ tinsert(br.engines.healingEngine.friend, SpecialCase)
+ br.engines.healingEngineCollections.SavedSpecialTargets[SpecialTargets[p]] = select(2, br.functions.unit:getGUID(SpecialTargets[p]))
end
end
end
for p = 1, #SpecialTargets do
local removedTarget = false
- for j = 1, #br.friend do
+ for j = 1, #br.engines.healingEngine.friend do
-- Trying to find a case of the unit inside the Main Table to remove
- if br.friend[j].unit == SpecialTargets[p] and (br.friend[j].guid ~= 0 and br.friend[j].guid ~= br._G.UnitGUID(SpecialTargets[p])) then
- tremove(br.friend, j)
+ if br.engines.healingEngine.friend[j].unit == SpecialTargets[p] and (br.engines.healingEngine.friend[j].guid ~= 0 and br.engines.healingEngine.friend[j].guid ~= br._G.UnitGUID(SpecialTargets[p])) then
+ tremove(br.engines.healingEngine.friend, j)
removedTarget = true
break
end
end
if removedTarget == true then
- for k, v in pairs(br.memberSetup.cache) do
+ for k, v in pairs(healingEngine.memberSetup.cache) do
-- Now we're trying to find that unit in the Cache table to remove
if SpecialTargets[p] == v.unit then
- br.memberSetup.cache[k] = nil
+ healingEngine.memberSetup.cache[k] = nil
end
end
end
end
- for i = 1, #br.friend do
+ for i = 1, #br.engines.healingEngine.friend do
-- We are updating all of the User Info (Health/Range/Name)
- br.friend[i]:UpdateUnit()
+ br.engines.healingEngine.friend[i]:UpdateUnit()
end
-- We are sorting by Health first
table.sort(
- br.friend,
+ br.engines.healingEngine.friend,
function(x, y)
return x.hp < y.hp
end
)
- if br.getOptionCheck("Prioritize Special Targets") == true then
- if br.GetUnitExists("focus") and br.memberSetup.cache[select(2, br.getGUID("focus"))] then
+ if br.functions.misc:getOptionCheck("Prioritize Special Targets") == true then
+ if br.functions.unit:GetUnitExists("focus") and healingEngine.memberSetup.cache[select(2, br.functions.unit:getGUID("focus"))] then
table.sort(
- br.friend,
+ br.engines.healingEngine.friend,
function(x)
if x.unit == "focus" then
return true
@@ -555,9 +574,9 @@ if not br.metaTable1 then
end
)
end
- if br.GetUnitExists("target") and br.memberSetup.cache[select(2, br.getGUID("target"))] then
+ if br.functions.unit:GetUnitExists("target") and healingEngine.memberSetup.cache[select(2, br.functions.unit:getGUID("target"))] then
table.sort(
- br.friend,
+ br.engines.healingEngine.friend,
function(x)
if x.unit == "target" then
return true
@@ -567,9 +586,9 @@ if not br.metaTable1 then
end
)
end
- if br.GetUnitExists("mouseover") and br.memberSetup.cache[select(2, br.getGUID("mouseover"))] then
+ if br.functions.unit:GetUnitExists("mouseover") and healingEngine.memberSetup.cache[select(2, br.functions.unit:getGUID("mouseover"))] then
table.sort(
- br.friend,
+ br.engines.healingEngine.friend,
function(x)
if x.unit == "mouseover" then
return true
@@ -580,15 +599,15 @@ if not br.metaTable1 then
)
end
end
- if br.pulseNovaDebugTimer == nil or br.pulseNovaDebugTimer <= GetTime() then
- br.pulseNovaDebugTimer = GetTime() + 0.5
- br.pulseNovaDebug()
+ if br.engines.healingEngineFrame.pulseNovaDebugTimer == nil or br.engines.healingEngineFrame.pulseNovaDebugTimer <= GetTime() then
+ br.engines.healingEngineFrame.pulseNovaDebugTimer = GetTime() + 0.5
+ br.engines.healingEngineFrame:pulseNovaDebug()
end
- -- update these frames to current br.friend values via a pulse in nova engine
+ -- update these frames to current br.engines.healingEngine.friend values via a pulse in nova engine
end
-- We are creating the initial Main Table
- br.friend()
+ br.engines.healingEngine.friend()
end
-- We are setting up the Tables for the first time
- br.SetupTables()
+ healingEngine:SetupTables()
end
diff --git a/System/engines/HealingEngineCollections.lua b/System/engines/HealingEngineCollections.lua
index aa6faba639..8e4718fa03 100644
--- a/System/engines/HealingEngineCollections.lua
+++ b/System/engines/HealingEngineCollections.lua
@@ -1,8 +1,11 @@
local _, br = ...
-br.novaEngineTables = { }
+br.engines.healingEngineCollections = br.engines.healingEngineCollections or {}
+local healingEngineCollections = br.engines.healingEngineCollections
+
+-- br.novaEngineTables = { }
-- This is for the Dispel Check, all Debuffs we want dispelled go here
-- valid arguments: stacks = num range = num
-br.novaEngineTables.DispelID = {
+healingEngineCollections.DispelID = {
[143579] = {stacks = 3 }, -- Immersius
[143434] = {stacks = 3 }, -- Fallen Protectors
[144514] = {stacks = 0 }, -- Norushen
@@ -121,7 +124,7 @@ br.novaEngineTables.DispelID = {
}
-- List of buffs to purge
-br.novaEngineTables.PurgeID = {
+healingEngineCollections.PurgeID = {
[282098] = true, -- Gift of Wind (Conclave)
[283619] = true, -- Wave of Light (Champion of Light)
[289623] = true, -- Guardian Spirit (BoD Trash)
@@ -162,14 +165,14 @@ br.novaEngineTables.PurgeID = {
[297133] = true, -- Defensive Countermeasure (Defense Bot Mk III)
}
-- List of debuffs that we should never dispell
-br.novaEngineTables.DoNotDispellList = {
+healingEngineCollections.DoNotDispellList = {
-- Midnight (Attumen the Huntsman)
{ id = 227404 }, -- Intangible Presence
{ id = 285000 }, -- https://www.wowhead.com/spell=285000/kelp-wrapped
--{ id = 284663}, --Bwonsamdi's Wrath
}
-- This is where we house the Debuffs that are bad for our users, and should not be healed when they have it
-br.novaEngineTables.BadDebuffList= {
+healingEngineCollections.BadDebuffList= {
[360618] = "Necrosis",
[104451] = "Ice Tomb",
[76577] = "Smoke Bomb",
@@ -208,7 +211,7 @@ br.novaEngineTables.BadDebuffList= {
[327992] = "Desolation", --Sire 100% healing reduction lasagna
}
-- list of special units we want to heal, these npc will go directly into healing engine(Special Heal must be checked)
-br.novaEngineTables.SpecialHealUnitList = {
+healingEngineCollections.SpecialHealUnitList = {
[71604] = "Contaminated Puddle",
[6459] = "Boss#3 SoO",
[6460] = "Boss#3 SoO",
@@ -222,8 +225,8 @@ br.novaEngineTables.SpecialHealUnitList = {
};
-- set dot that need to be healed to max(needs to be topped) to very low values so that engine will prioritize them
-- the value used here will be substract from current health, we could use negative values to add back health instead
--- these are checked debuff on allies ie br.friend[i].unit wear 145263 and its hp is 70, engine will use 50 instead
-br.novaEngineTables.SpecificHPDebuffs = {
+-- these are checked debuff on allies ie br.engines.healingEngine.friend[i].unit wear 145263 and its hp is 70, engine will use 50 instead
+healingEngineCollections.SpecificHPDebuffs = {
--[123456, value = 20, stacks = 1 }, -- Exemple.
--[123456, value = -100, stacks = 3 }, -- Exemple
-- Twin Ogrons
@@ -293,7 +296,7 @@ br.novaEngineTables.SpecificHPDebuffs = {
}
-- this table will assign role to any unit wearing the unit name
-br.novaEngineTables.roleTable = {
+healingEngineCollections.roleTable = {
["Oto the Protector"] = { role = "TANK", class = "Warrior" }, -- proving grounds tank
["Sooli the Survivalist"] = { role = "DPS", class = "Hunter" }, -- proving grounds dps
["Ki the Assassin"] = { role = "DPS", class = "Rogue" }, -- proving grounds dps
@@ -304,13 +307,13 @@ br.novaEngineTables.roleTable = {
["Primal Earth Elemental"] = {role = "TANK", class = "Warrior"}
}
-- special targets to include when we want to heal npcs
-br.novaEngineTables.SavedSpecialTargets = {
+healingEngineCollections.SavedSpecialTargets = {
["target"] = nil,
["mouseover"] = nil,
["focus"] = nil,
}
-- ToDo: we need a powerful DoT handler to handle stuff such as hand of purity/heal over time
-br.novaEngineTables.BadlyDeBuffed = {
+healingEngineCollections.BadlyDeBuffed = {
--High Maul
--Kargath
159386, --http://www.wowhead.com/spell=159386/iron-bomb
@@ -336,7 +339,7 @@ br.novaEngineTables.BadlyDeBuffed = {
}
-- Table for NPCs we do not want to add to table (eg. Hymdal/Odyn after they become friendly)
-br.novaEngineTables.skipNPC = {
+healingEngineCollections.skipNPC = {
"95676", -- Odyn
"94960", -- Hynmdall
"100482", -- Senegos
diff --git a/System/engines/HealingEngineFrame.lua b/System/engines/HealingEngineFrame.lua
index f2cca5d61a..9642b05960 100644
--- a/System/engines/HealingEngineFrame.lua
+++ b/System/engines/HealingEngineFrame.lua
@@ -1,6 +1,9 @@
local _, br = ...
-function br.pulseNovaDebug()
- -- if br.getOptionCheck("Healing Debug") ~= true then
+br.engines.healingEngineFrame = br.engines.healingEngineFrame or {}
+local healingEngineFrame = br.engines.healingEngineFrame
+
+function healingEngineFrame:pulseNovaDebug()
+ -- if br.functions.misc:getOptionCheck("Healing Debug") ~= true then
-- -- if healingDebug--[[Started]] and br._G["healingDebugFrame"]:IsShown() == true then
-- -- _G["healingDebugFrame"]:Hide()
-- -- end
@@ -14,26 +17,26 @@ function br.pulseNovaDebug()
-- br.ui:showWindow("healing")
-- if not healingDebug then --Started then
-- healingDebug = true --Started = true
- -- -- frameCreation("healingDebug",200,150,"|cffFF001Ebr.friend")
+ -- -- frameCreation("healingDebug",200,150,"|cffFF001Ebr.engines.healingEngine.friend")
-- br.ui:createHealingWindow()
-- for i = 1, 5 do
-- local thisOption = { name = i, status = 100, statusMin = 0, statusMax = 100, unit = "thisUnit" }
-- createNovaStatusBar("healingDebug",thisOption,10,-i*25,180,20,false)
-- end
-- end
- -- -- i will gather frames informations via thisDebugRow = br.friendDebug[i]
- -- local novaUnits = #br.friend
+ -- -- i will gather frames informations via thisDebugRow = br.engines.healingEngine.friendDebug[i]
+ -- local novaUnits = #br.engines.healingEngine.friend
-- if novaUnits > 5 then
-- novaUnits = 5
-- end
-- for i = 1, novaUnits do
- -- local thisUnit = br.friend[i]
- -- _G[br.friendDebug[i]]:Show()
- -- thisDebugRow = br.friendDebug[i]
+ -- local thisUnit = br.engines.healingEngine.friend[i]
+ -- _G[br.engines.healingEngine.friendDebug[i]]:Show()
+ -- thisDebugRow = br.engines.healingEngine.friendDebug[i]
-- _G[thisDebugRow]:SetValue(thisUnit.hp)
-- _G[thisDebugRow.."Text"]:SetText(math.floor(thisUnit.hp))
- -- if br.classColors[thisUnit.class] ~= nil then
- -- _G[thisDebugRow]:SetStatusBarTexture(br.classColors[thisUnit.class].R,br.classColors[thisUnit.class].G,br.classColors[thisUnit.class].B)
+ -- if br.ui.colors.classs[thisUnit.class] ~= nil then
+ -- _G[thisDebugRow]:SetStatusBarTexture(br.ui.colors.classs[thisUnit.class].R,br.ui.colors.classs[thisUnit.class].G,br.ui.colors.classs[thisUnit.class].B)
-- else
-- _G[thisDebugRow]:SetStatusBarTexture(1,1,1)
-- end
@@ -41,11 +44,11 @@ function br.pulseNovaDebug()
-- _G[thisDebugRow]:SetStatusBarTexture(0.70,0,0)
-- end
-- end
- -- -- show up to 5 frames or #br.friend
+ -- -- show up to 5 frames or #br.engines.healingEngine.friend
-- if novaUnits < 5 then
-- for i = 1, 5 do
-- if i > novaUnits then
- -- _G[br.friendDebug[i]]:Hide()
+ -- _G[br.engines.healingEngine.friendDebug[i]]:Hide()
-- end
-- end
-- end
@@ -71,7 +74,7 @@ function br.pulseNovaDebug()
-- end
-- end
-- local y = howManyBoxes
- -- if y ~= 1 then y = ((y-1) * -br.spacing) -5 end
+ -- if y ~= 1 then y = ((y-1) * -15) -5 end
-- if y == 1 then y = -5 end
-- if hideCheckbox then
@@ -85,7 +88,7 @@ function br.pulseNovaDebug()
-- -- Set Steps
-- newBar.settings.min = min or 0
-- newBar.settings.max = max or 100
- -- newBar.settings.step = br.getHP(unit) or 0
+ -- newBar.settings.step = br.functions.unit:getHP(unit) or 0
newHPBar:SetParent(parent.content)
@@ -94,21 +97,22 @@ function br.pulseNovaDebug()
newHPBar:SetSize(180, 22)
local color = "|cffFFFFFF"
- if br.classColors[unit.class] ~= nil then
- color = br.classColors[unit.class].hex
+ local classColor = br.ui.colors:getColor(unit.unit)
+ if classColor then
+ color = classColor.hex
end
newHPBar:SetColor(color)
-- Read number from config or set default
- if br.data.settings[br.selectedSpec][br.selectedProfile][text .. "Status"] == nil then
- br.data.settings[br.selectedSpec][br.selectedProfile][text .. "Status"] = br.getHP(unit) or 0
+ if br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile][text .. "Status"] == nil then
+ br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile][text .. "Status"] = br.functions.unit:getHP(unit) or 0
end
- local state = br.data.settings[br.selectedSpec][br.selectedProfile][text .. "Status"]
+ local state = br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile][text .. "Status"]
br.newHpBar:SetValue(state, 0, 100)
-- Event: OnValueChange
-- newBar:SetEventListener('OnValueChanged', function(this, event, checked)
- -- br.data.settings[br.selectedSpec][br.selectedProfile][text.."Status"] = newSpinner:GetNumber()
+ -- br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile][text.."Status"] = newSpinner:GetNumber()
-- end)
-- Event: Tooltip
if tooltip or tooltipSpin then
@@ -140,107 +144,107 @@ function br.pulseNovaDebug()
return newHPBar
end
- function br.createNovaStatusBar(parent, option, x, y, width, height)
- local value, status, statusMin, statusMax = option.name, option.status, option.statusMin, option.statusMax
- width = width or 180
- height = height or 22
- if br._G[parent .. value .. "Nova"] == nil then
- local scale = 1 --br.data.settings.optionsFrame.scale or 1
- local currentValue = _G[parent .. value .. "Nova"]:GetValue()
- _G[parent .. value .. "Nova"] = br._G.CreateFrame("StatusBar", br._G[parent .. value .. "Nova"],
- br._G.UIParent, "BackdropTemplate")
- _G[parent .. value .. "Nova"]:SetWidth(width * scale)
- _G[parent .. value .. "Nova"]:SetHeight(height * scale)
- _G[parent .. value .. "Nova"]:SetPoint("TOPLEFT", x * scale, (y - 2) * scale)
- _G[parent .. value .. "Nova"]:SetAlpha(1)
- -- status part
- _G[parent .. value .. "Nova"]:SetStatusBarTexture(1, 1, 1)
- _G[parent .. value .. "Nova"]:SetMinMaxValues(statusMin, statusMax)
- _G[parent .. value .. "Nova"]:SetValue(currentValue or 50)
- -- hover event
- _G[parent .. value .. "Nova"]:SetBackdropColor(1, 1, 1)
+ function healingEngineFrame:createNovaStatusBar(parent, option, x, y, width, height)
+ -- local value, status, statusMin, statusMax = option.name, option.status, option.statusMin, option.statusMax
+ -- width = width or 180
+ -- height = height or 22
+ -- if br._G[parent .. value .. "Nova"] == nil then
+ -- local scale = 1 --br.data.settings.optionsFrame.scale or 1
+ -- local currentValue = 0--_G[parent .. value .. "Nova"]:GetValue()
+ -- _G[parent .. value .. "Nova"] = br._G.CreateFrame("StatusBar", br._G[parent .. value .. "Nova"],
+ -- br._G.UIParent, "BackdropTemplate")
+ -- _G[parent .. value .. "Nova"]:SetWidth(width * scale)
+ -- _G[parent .. value .. "Nova"]:SetHeight(height * scale)
+ -- _G[parent .. value .. "Nova"]:SetPoint("TOPLEFT", x * scale, (y - 2) * scale)
+ -- _G[parent .. value .. "Nova"]:SetAlpha(1)
+ -- -- status part
+ -- _G[parent .. value .. "Nova"]:SetStatusBarTexture(1, 1, 1)
+ -- _G[parent .. value .. "Nova"]:SetMinMaxValues(statusMin, statusMax)
+ -- _G[parent .. value .. "Nova"]:SetValue(currentValue or 50)
+ -- -- hover event
+ -- _G[parent .. value .. "Nova"]:SetBackdropColor(1, 1, 1)
- _G[parent .. value .. "Nova"]:SetScript(
- "OnEnter",
- function(self)
- ---@diagnostic disable-next-line: undefined-field
- br._G.GameTooltip:SetOwner(self, "BOTTOMLEFT", 225, 5)
- local thisUnit = br.friend[value]
- if thisUnit then
- local color = "|cffFFFFFF"
- for i = 1, #br.classColors do
- if br.classColors[i].class == thisUnit.class then
- color = "|cff" .. br.classColors[i].hex
- end
- end
- ---@diagnostic disable-next-line: undefined-field
- br._G.GameTooltip:SetText(
- color ..
- "Name: " ..
- thisUnit.name ..
- "\n|cffFF1100Health: " .. math.floor(thisUnit.hp) .. "\n|cff11A7DFRole: " .. thisUnit.role,
- nil,
- nil,
- nil,
- nil,
- true
- )
- ---@diagnostic disable-next-line: undefined-field
- br._G.GameTooltip:Show()
- end
- end
- )
- -- leave event
- _G[parent .. value .. "Nova"]:SetScript(
- "OnLeave",
- function(self)
- ---@diagnostic disable-next-line: undefined-field
- br._G.GameTooltip:Hide()
- end
- )
- -- leave event
- _G[parent .. value .. "Nova"]:SetScript(
- "OnMouseDown",
- function(self, button)
- if button == "RightButton" then
- br._G.FocusUnit(br.friend[value].unit)
- else
- br._G.RunMacroText("/target " .. br._G.UnitName(br.friend[value].unit))
- end
- end
- )
- -- text
- _G[parent .. value .. "NovaText"] = br._G[parent .. value .. "Nova"]:CreateFontString(
- _G[parent .. value .. "NovaText"], "ARTWORK")
- _G[parent .. value .. "NovaText"]:SetWidth(width * scale)
- _G[parent .. value .. "NovaText"]:SetHeight(height * scale)
- _G[parent .. value .. "NovaText"]:SetPoint("CENTER", 0, -2)
- _G[parent .. value .. "NovaText"]:SetAlpha(1)
- _G[parent .. value .. "NovaText"]:SetJustifyH("CENTER")
- _G[parent .. value .. "NovaText"]:SetFont(br.data.settings.font, br.data.settings.fontsize, "THICKOUTLINE")
- _G[parent .. value .. "NovaText"]:SetText(currentValue, nil, nil, nil, nil, false)
- if br.nNovaDebug == nil then
- br.nNovaDebug = {}
- end
- br.nNovaDebug[#br.nNovaDebug + 1] = parent .. value .. "Nova"
- end
+ -- _G[parent .. value .. "Nova"]:SetScript(
+ -- "OnEnter",
+ -- function(self)
+ -- ---@diagnostic disable-next-line: undefined-field
+ -- br._G.GameTooltip:SetOwner(self, "BOTTOMLEFT", 225, 5)
+ -- local thisUnit = br.engines.healingEngine.friend[value]
+ -- if thisUnit then
+ -- local color = "|cffFFFFFF"
+ -- for i = 1, #br.ui.colors.classs do
+ -- if br.ui.colors.classs[i].class == thisUnit.class then
+ -- color = "|cff" .. br.ui.colors.classs[i].hex
+ -- end
+ -- end
+ -- ---@diagnostic disable-next-line: undefined-field
+ -- br._G.GameTooltip:SetText(
+ -- color ..
+ -- "Name: " ..
+ -- thisUnit.name ..
+ -- "\n|cffFF1100Health: " .. math.floor(thisUnit.hp) .. "\n|cff11A7DFRole: " .. thisUnit.role,
+ -- nil,
+ -- nil,
+ -- nil,
+ -- nil,
+ -- true
+ -- )
+ -- ---@diagnostic disable-next-line: undefined-field
+ -- br._G.GameTooltip:Show()
+ -- end
+ -- end
+ -- )
+ -- -- leave event
+ -- _G[parent .. value .. "Nova"]:SetScript(
+ -- "OnLeave",
+ -- function(self)
+ -- ---@diagnostic disable-next-line: undefined-field
+ -- br._G.GameTooltip:Hide()
+ -- end
+ -- )
+ -- -- leave event
+ -- _G[parent .. value .. "Nova"]:SetScript(
+ -- "OnMouseDown",
+ -- function(self, button)
+ -- if button == "RightButton" then
+ -- br._G.FocusUnit(br.engines.healingEngine.friend[value].unit)
+ -- else
+ -- br._G.RunMacroText("/target " .. br._G.UnitName(br.engines.healingEngine.friend[value].unit))
+ -- end
+ -- end
+ -- )
+ -- -- text
+ -- _G[parent .. value .. "NovaText"] = br._G[parent .. value .. "Nova"]:CreateFontString(
+ -- _G[parent .. value .. "NovaText"], "ARTWORK")
+ -- _G[parent .. value .. "NovaText"]:SetWidth(width * scale)
+ -- _G[parent .. value .. "NovaText"]:SetHeight(height * scale)
+ -- _G[parent .. value .. "NovaText"]:SetPoint("CENTER", 0, -2)
+ -- _G[parent .. value .. "NovaText"]:SetAlpha(1)
+ -- _G[parent .. value .. "NovaText"]:SetJustifyH("CENTER")
+ -- _G[parent .. value .. "NovaText"]:SetFont(br.data.settings.font, br.data.settings.fontsize, "THICKOUTLINE")
+ -- _G[parent .. value .. "NovaText"]:SetText(currentValue, nil, nil, nil, nil, false)
+ -- if br.nNovaDebug == nil then
+ -- br.nNovaDebug = {}
+ -- end
+ -- br.nNovaDebug[#br.nNovaDebug + 1] = parent .. value .. "Nova"
+ -- end
end
- local novaUnits = #br.friend
+ local novaUnits = #br.engines.healingEngine.friend
if novaUnits > 5 then
novaUnits = 5
end
- if br.getOptionCheck("Healing Debug") then
- -- br.data.settings[br.selectedSpec]["healing"].height = ((novaUnits + 1) * 23) --:SetHeight
+ if br.functions.misc:getOptionCheck("Healing Debug") then
+ -- br.data.settings[br.loader.selectedSpec]["healing"].height = ((novaUnits + 1) * 23) --:SetHeight
-- textString = ""
- -- for i = 1, #br.friend do
- -- local thisUnit = br.friend[i]
+ -- for i = 1, #br.engines.healingEngine.friend do
+ -- local thisUnit = br.engines.healingEngine.friend[i]
-- local color = "|cffFFFFFF"
-- if classColors[thisUnit.class] ~= nil then
-- color = "|"..classColors[thisUnit.class].hex
-- end
-- -- if textString == nil then
- -- textString = textString.."\nName: "..color..UnitName(br.friend[i].unit).." |r HP:"..thisUnit.hp
+ -- textString = textString.."\nName: "..color..UnitName(br.engines.healingEngine.friend[i].unit).." |r HP:"..thisUnit.hp
-- end
-- br.ui.window.healing:AddMessage(textString)
-- if not br.ui.window["healing"]["parent"] then
@@ -249,42 +253,35 @@ function br.pulseNovaDebug()
--br.ui:showWindow("healing")
-- if not healingDebug then --Started then
-- healingDebug = true --Started = true
- -- frameCreation("healingDebug",200,150,"|cffFF001Ebr.friend")
+ -- frameCreation("healingDebug",200,150,"|cffFF001Ebr.engines.healingEngine.friend")
-- br.ui:createHealingWindow()
for i = 1, 5 do
local thisOption = { name = i, status = 100, statusMin = 0, statusMax = 100, unit = "thisUnit" }
-- br.ui.window.healing:AddMessage(thisOption)
- br.createNovaStatusBar("healing", thisOption, 10, -i * 25, 180, 20)
+ healingEngineFrame:createNovaStatusBar("healing", thisOption, 10, -i * 25, 180, 20)
--br.ui:createBar("healing", thisOption, thisOption.unit, tooltip, tooltipSpin)
end
-- end
- -- i will gather frames informations via thisDebugRow = br.friendDebug[i]
+ -- i will gather frames informations via thisDebugRow = br.engines.healingEngine.friendDebug[i]
for i = 1, novaUnits do
- local thisUnit = br.friend[i]
+ local thisUnit = br.engines.healingEngine.friend[i]
local thisDebugRow
- -- local color = "|cffFFFFFF"
- local class
if thisUnit.class == "Death Knight" then
thisUnit.class = "Deathknight"
elseif thisUnit.class == "Demon Hunter" then
thisUnit.class = "Demonhunter"
end
- for i = 1, #br.classColors do
- if br.classColors[i].class == thisUnit.class then
- class = i
- end
- end
+ local classColor = br.ui.colors:getColor(thisUnit.unit)
-- local tooltipMsg = color.."Name: "..thisUnit.name..
-- "\n|cffFF1100Health: "..math.floor(thisUnit.hp)..
-- "\n|cff11A7DFRole: "..thisUnit.role, nil, nil, nil, nil, true
-- br.ui:createBar("healing", UnitName(thisUnit), thisUnit, tooltipMsg)
- _G[br.nNovaDebug[i]]:Show()
+ -- _G[br.nNovaDebug[i]]:Show()
thisDebugRow = br.nNovaDebug[i]
_G[thisDebugRow]:SetValue(thisUnit.hp)
_G[thisDebugRow .. "Text"]:SetText(math.floor(thisUnit.hp))
- if br.classColors[class] ~= nil then
- _G[thisDebugRow]:SetStatusBarTexture(br.classColors[class].R, br.classColors[class].G,
- br.classColors[class].B)
+ if classColor then
+ _G[thisDebugRow]:SetStatusBarTexture(classColor.R, classColor.G, classColor.B)
else
_G[thisDebugRow]:SetStatusBarTexture(1, 1, 1)
end
@@ -292,7 +289,7 @@ function br.pulseNovaDebug()
_G[thisDebugRow]:SetStatusBarTexture(0.70, 0, 0)
end
end
- -- show up to 5 frames or #br.friend
+ -- show up to 5 frames or #br.engines.healingEngine.friend
if novaUnits < 5 then
for i = 1, 5 do
if i > novaUnits then
@@ -300,20 +297,20 @@ function br.pulseNovaDebug()
end
end
end
- elseif not br.getOptionCheck("Healing Debug") then
+ elseif not br.functions.misc:getOptionCheck("Healing Debug") then
for i = 1, novaUnits do
if br.nNovaDebug ~= nil then
_G[br.nNovaDebug[i]]:Hide()
end
end
- elseif br.ui.window["healing"]["parent"] and br.data.settings[br.selectedSpec]["healing"].active == true then
+ elseif br.ui.window["healing"]["parent"] and br.data.settings[br.loader.selectedSpec]["healing"].active == true then
br.ui:closeWindow("healing")
end
end
--- if br.getOptionCheck("Rotation Log") then
+-- if br.functions.misc:getOptionCheck("Rotation Log") then
-- if not br.ui.window['debug']['parent'] then br.ui:createDebugWindow() end
-- br.ui:showWindow("debug")
--- elseif br.ui.window['debug']['parent'] and br.data.settings[br.selectedSpec]["debug"].active == true then
+-- elseif br.ui.window['debug']['parent'] and br.data.settings[br.loader.selectedSpec]["debug"].active == true then
-- br.ui:closeWindow("debug")
-- end
diff --git a/System/engines/HealingEngineFunctions.lua b/System/engines/HealingEngineFunctions.lua
index 5e8d863a15..73d9124189 100644
--- a/System/engines/HealingEngineFunctions.lua
+++ b/System/engines/HealingEngineFunctions.lua
@@ -1,11 +1,14 @@
local _, br = ...
+br.engines.healingEngineFunctions = br.engines.healingEngineFunctions or {}
+local healingEngineFunctions = br.engines.healingEngineFunctions
+
-- here we want to define functions to use with the healing profiles
-- find best tank to put our lb/beacon/earth shield on
-function br.getFocusedTank()
- local tanks = br.getTanksTable()
+function healingEngineFunctions:getFocusedTank()
+ local tanks = br.engines.healingEngineFunctions:getTanksTable()
-- if we are targetting a mob and its targetting a tank we want to define which tank it is.
- if #tanks > 0 and br.GetUnitExists("target") and br.GetUnitIsVisible("target") and br.GetUnitExists("targettarget")
- and br.GetUnitIsVisible("targettarget") then
+ if #tanks > 0 and br.functions.unit:GetUnitExists("target") and br.functions.unit:GetUnitIsVisible("target") and br.functions.unit:GetUnitExists("targettarget")
+ and br.functions.unit:GetUnitIsVisible("targettarget") then
local targetTargetGUID = br._G.UnitGUID("targettarget")
for i = 1,#tanks do
if tanks[i].guid == targetTargetGUID then
@@ -26,12 +29,12 @@ function br.getFocusedTank()
end
end
-- find tanks
-function br.getTanksTable()
+function healingEngineFunctions:getTanksTable()
local tanksTable = {}
- for i = 1, #br.friend do
- if br.friend[i] then
- if br.friend[i].role == "TANK" then
- br._G.tinsert(tanksTable, br.friend[i])
+ for i = 1, #br.engines.healingEngine.friend do
+ if br.engines.healingEngine.friend[i] then
+ if br.engines.healingEngine.friend[i].role == "TANK" then
+ br._G.tinsert(tanksTable, br.engines.healingEngine.friend[i])
end
end
end
@@ -42,24 +45,24 @@ function br.getTanksTable()
return tanksTable
end
-- we want to define an iteration that will compare allies to heal in range of enemies or allies
-function br.castWiseAoEHeal(unitTable,spell,radius,health,minCount,maxCount,facingCheck,movementCheck)
- if movementCheck ~= true or not br.isMoving("player") then
+function healingEngineFunctions:castWiseAoEHeal(unitTable,spell,radius,health,minCount,maxCount,facingCheck,movementCheck)
+ if movementCheck ~= true or not br.functions.misc:isMoving("player") then
local bestCandidate = nil
-- find best candidate with list of units
for i = 1, #unitTable do
-- added a visible check as its not in healing engine.
- if (br.GetUnitIsVisible(unitTable[i].unit) and (facingCheck ~= true or br.getFacing("player",unitTable[i].unit)) or br.GetUnitIsUnit(unitTable[i].unit,"player")) then
- local candidate = br.getUnitsToHealAround(unitTable[i].unit,radius,health,maxCount)
+ if (br.functions.unit:GetUnitIsVisible(unitTable[i].unit) and (facingCheck ~= true or br.functions.unit:getFacing("player",unitTable[i].unit)) or br.functions.unit:GetUnitIsUnit(unitTable[i].unit,"player")) then
+ local candidate = br.engines.healingEngineFunctions:getUnitsToHealAround(unitTable[i].unit,radius,health,maxCount)
if bestCandidate == nil or bestCandidate[0].coef > candidate[0].coef then
bestCandidate = candidate
end
end
end
-- if we meet count minimum then we cast
- if bestCandidate ~= nil and #bestCandidate >= minCount and br.getLineOfSight("player",bestCandidate[0].unit) and br.getDistance("player",bestCandidate[0].unit) <= 40 then
+ if bestCandidate ~= nil and #bestCandidate >= minCount and br.functions.misc:getLineOfSight("player",bestCandidate[0].unit) and br.functions.range:getDistance("player",bestCandidate[0].unit) <= 40 then
-- here we would like instead to cast on unit
- if br.castSpell(bestCandidate[0].unit,spell,facingCheck,movementCheck) then
- if br._G.IsAoEPending() then br._G.SpellStopTargeting() br.addonDebug("Canceling Spell", true) end
+ if br.functions.cast:castSpell(bestCandidate[0].unit,spell,facingCheck,movementCheck) then
+ if br._G.IsAoEPending() then br._G.SpellStopTargeting() br.functions.misc:addonDebug("Canceling Spell", true) end
return true
end
end
@@ -67,7 +70,7 @@ function br.castWiseAoEHeal(unitTable,spell,radius,health,minCount,maxCount,faci
return false
end
-- function to define range between players in tables.
-function br.getNovaDistance(Unit1,Unit2)
+function healingEngineFunctions:getNovaDistance(Unit1,Unit2)
-- if we are comparing same unit return 0
if Unit1.guid == Unit2.guid then
return 0
@@ -85,25 +88,25 @@ function br.getNovaDistance(Unit1,Unit2)
return 1000
end
end
--- now that we have our units in br.friend with their positions, we can compare them to our enemies
+-- now that we have our units in br.engines.healingEngine.friend with their positions, we can compare them to our enemies
-- we will send them as object args to our distance functions... since both engines have x and y, we will be able to mix them up
-function br.getUnitsToHealAround(UnitID,radius,health,count)
+function healingEngineFunctions:getUnitsToHealAround(UnitID,radius,health,count)
-- if we provide an unitID, we get this units location once
local X1,Y1,Z1 = 0,0,0
-- if an unit of type string is passed we consider it as a UnitID
if type(UnitID) == "string" then
- X1,Y1,Z1 = br.GetObjectPosition(UnitID)
- -- if we provide it a table, take that object position(accepts br.enemy[i] or br.friend[i])
+ X1,Y1,Z1 = br.functions.unit:GetObjectPosition(UnitID)
+ -- if we provide it a table, take that object position(accepts br.engines.enemiesEngine.enemy[i] or br.engines.healingEngine.friend[i])
elseif UnitID and UnitID.x ~= 0 then
X1,Y1,Z1 = UnitID.x,UnitID.y,UnitID.z
end
local unit = {x = X1,y = Y1,z = Z1,guid = br._G.UnitGUID(UnitID),name = br._G.UnitName(UnitID)}
-- once we get our unit location we call our getdistance
local lowHealthCandidates = {}
- for i = 1, #br.friend do
- local thisUnit = br.friend[i]
+ for i = 1, #br.engines.healingEngine.friend do
+ local thisUnit = br.engines.healingEngine.friend[i]
-- if in given radius
- if thisUnit.hp <= health and br.getNovaDistance(unit,thisUnit) < radius then
+ if thisUnit.hp <= health and br.engines.healingEngineFunctions:getNovaDistance(unit,thisUnit) < radius then
-- if its first item in table, insert
if #lowHealthCandidates == 0 then
br._G.tinsert(lowHealthCandidates, 1, {hp = thisUnit.hp,x = thisUnit.x,y = thisUnit.y,z = thisUnit.z,name = thisUnit.name,guid = thisUnit.guid})
@@ -133,7 +136,7 @@ function br.getUnitsToHealAround(UnitID,radius,health,count)
end
end
end
- lowHealthCandidates[0] = { unit = UnitID, coef = br.getLowHealthCoeficient(lowHealthCandidates) }
+ lowHealthCandidates[0] = { unit = UnitID, coef = br.engines.healingEngineFunctions:getLowHealthCoeficient(lowHealthCandidates) }
return lowHealthCandidates
end
@@ -142,10 +145,10 @@ local startUnit
local currentJump
local jumpFound = true
local function chainJumps(unit,hp,range)
- for i = 1, #br.friend do
- local newUnit = br.friend[i]
+ for i = 1, #br.engines.healingEngine.friend do
+ local newUnit = br.engines.healingEngine.friend[i]
local unitFound = false
- if newUnit.hp <= hp and newUnit.guid ~= unit.guid and br.getNovaDistance(unit,newUnit) <= range then
+ if newUnit.hp <= hp and newUnit.guid ~= unit.guid and br.engines.healingEngineFunctions:getNovaDistance(unit,newUnit) <= range then
for j = 1, #totalUnits do
if totalUnits[j].guid == newUnit.guid then
unitFound = true
@@ -162,10 +165,10 @@ local function chainJumps(unit,hp,range)
end
-function br.chainHealUnits(spell,range,hp,count)
+function healingEngineFunctions:chainHealUnits(spell,range,hp,count)
jumpFound = true
- for i = 1, #br.friend do
- local thisUnit = br.friend[i]
+ for i = 1, #br.engines.healingEngine.friend do
+ local thisUnit = br.engines.healingEngine.friend[i]
if thisUnit.hp <= hp then
startUnit = thisUnit.unit
currentJump = thisUnit
@@ -177,7 +180,7 @@ function br.chainHealUnits(spell,range,hp,count)
totalUnits = {}
elseif #totalUnits >= count then
totalUnits= {}
- if br.castSpell(startUnit,spell,false,true) then
+ if br.functions.cast:castSpell(startUnit,spell,false,true) then
return true
end
end
@@ -186,7 +189,7 @@ function br.chainHealUnits(spell,range,hp,count)
end
-- returns coefficient of low health units around another unit
-function br.getLowHealthCoeficient(lowHealthCandidates)
+function healingEngineFunctions:getLowHealthCoeficient(lowHealthCandidates)
local tableCoef = 0
local lowHealthCount = #lowHealthCandidates
-- if critical status, add it more coef
@@ -202,24 +205,24 @@ function br.getLowHealthCoeficient(lowHealthCandidates)
end
-- old design with so many objectmanager queries it was wrong.
-- if getAllies("player",40) > 5 then
-function br.getAllies(Unit,Radius)
+function healingEngineFunctions:getAllies(Unit,Radius)
local alliesTable = {}
- for i=1,#br.friend do
- if not br.GetUnitIsDeadOrGhost(br.friend[i].unit) then
- if br.getDistance(Unit,br.friend[i].unit) <= Radius then
- br._G.tinsert(alliesTable,br.friend[i])
+ for i=1,#br.engines.healingEngine.friend do
+ if not br.functions.unit:GetUnitIsDeadOrGhost(br.engines.healingEngine.friend[i].unit) then
+ if br.functions.range:getDistance(Unit,br.engines.healingEngine.friend[i].unit) <= Radius then
+ br._G.tinsert(alliesTable,br.engines.healingEngine.friend[i])
end
end
end
return alliesTable
end
-- if getAlliesInLocation("player",X,Y,Z) > 5 then
-function br.getAlliesInLocation(myX,myY,myZ,Radius)
+function healingEngineFunctions:getAlliesInLocation(myX,myY,myZ,Radius)
local alliesTable = {}
- for i=1,#br.friend do
- if not br.GetUnitIsDeadOrGhost(br.friend[i].unit) then
- if br.getDistanceToLocation(br.friend[i].unit,myX,myY,myZ) <= Radius then
- br._G.tinsert(alliesTable,br.friend[i].unit)
+ for i=1,#br.engines.healingEngine.friend do
+ if not br.functions.unit:GetUnitIsDeadOrGhost(br.engines.healingEngine.friend[i].unit) then
+ if br.functions.range:getDistanceToLocation(br.engines.healingEngine.friend[i].unit,myX,myY,myZ) <= Radius then
+ br._G.tinsert(alliesTable,br.engines.healingEngine.friend[i].unit)
end
end
end
@@ -228,43 +231,43 @@ end
--We check if unit has a badly debuff thats need a personal CD to survive or reduce damage
--Todo check for more details for example how much damage are we going to take and whats the best spell to use.
--For example we can check if we are going to die or not.
-function br.isBadlyDeBuffed(Unit)
+function healingEngineFunctions:isBadlyDeBuffed(Unit)
if Unit == nil then
return false
end
- for i=1,#br.novaEngineTables.BadlyDeBuffed do
- if br.UnitDebuffID(Unit,br.novaEngineTables.BadlyDeBuffed[i])~=nil then
+ for i=1,#br.engines.healingEngineCollections.BadlyDeBuffed do
+ if br.functions.aura:UnitDebuffID(Unit,br.engines.healingEngineCollections.BadlyDeBuffed[i])~=nil then
return true
end
end
return false
end
-function br.inLoSHealer()
+function healingEngineFunctions:inLoSHealer()
local function drawHealers(healer)
local LibDraw = br._G.LibStub("LibDraw-BR")
- -- local facing = br.GetObjectFacing("player")
- local playerX, playerY, playerZ = br.GetObjectPosition("player")
- -- local locateX, locateY, locateZ = br.GetObjectPosition(healer)
- local healerX, healerY, healerZ = br.GetObjectPosition(healer)
- if br.getLineOfSight("player",healer) then
+ -- local facing = br.functions.unit:GetObjectFacing("player")
+ local playerX, playerY, playerZ = br.functions.unit:GetObjectPosition("player")
+ -- local locateX, locateY, locateZ = br.functions.unit:GetObjectPosition(healer)
+ local healerX, healerY, healerZ = br.functions.unit:GetObjectPosition(healer)
+ if br.functions.misc:getLineOfSight("player",healer) then
LibDraw.SetColor(0, 255, 0)
else
LibDraw.SetColor(255, 0, 0)
end
return LibDraw.Line(playerX, playerY, playerZ, healerX, healerY, healerZ)
end
- for i = 1, #br.friend do
- local thisUnit = br.friend[i].unit
- if not br.GetUnitIsUnit(thisUnit,"player") and br._G.UnitGroupRolesAssigned(thisUnit) == "HEALER" then
+ for i = 1, #br.engines.healingEngine.friend do
+ local thisUnit = br.engines.healingEngine.friend[i].unit
+ if not br.functions.unit:GetUnitIsUnit(thisUnit,"player") and br._G.UnitGroupRolesAssigned(thisUnit) == "HEALER" then
drawHealers(thisUnit)
end
end
end
-function br.isInside(x,y,ax,ay,bx,by,dx,dy)
+function healingEngineFunctions:isInside(x,y,ax,ay,bx,by,dx,dy)
local bax = bx - ax
local bay = by - ay
local dax = dx - ax
@@ -278,10 +281,10 @@ function br.isInside(x,y,ax,ay,bx,by,dx,dy)
return true
end
-function br.getUnitsInRect(width,length, showLines, hp)
+function healingEngineFunctions:getUnitsInRect(width,length, showLines, hp)
local LibDraw = br._G.LibStub("LibDraw-BR")
- local playerX, playerY, playerZ = br.GetObjectPosition("player")
- local facing = br.GetObjectFacing("player")
+ local playerX, playerY, playerZ = br.functions.unit:GetObjectPosition("player")
+ local facing = br.functions.unit:GetObjectFacing("player")
-- Near Left
local nlX, nlY, nlZ = br._G.GetPositionFromPosition(playerX, playerY, playerZ, width/2, facing + math.rad(90), 0)
-- Near Right
@@ -308,12 +311,12 @@ function br.getUnitsInRect(width,length, showLines, hp)
local unitCounter = 0
local UnitsInRect = {}
_G.table.wipe(UnitsInRect)
- for i = 1, #br.friend do
- local thisUnit = br.friend[i]
- if br.GetUnitExists(thisUnit.unit) and thisUnit.hp <= hp and not br.GetUnitIsDeadOrGhost(thisUnit.unit) then
+ for i = 1, #br.engines.healingEngine.friend do
+ local thisUnit = br.engines.healingEngine.friend[i]
+ if br.functions.unit:GetUnitExists(thisUnit.unit) and thisUnit.hp <= hp and not br.functions.unit:GetUnitIsDeadOrGhost(thisUnit.unit) then
local tX, tY = thisUnit.x, thisUnit.y
if tX and tY then
- if br.isInside(tX,tY,nlX,nlY,nrX,nrY,frX,frY) then
+ if br.engines.healingEngineFunctions:isInside(tX,tY,nlX,nlY,nrX,nrY,frX,frY) then
if showLines then
LibDraw.Circle(tX, tY, playerZ, br._G.UnitBoundingRadius(thisUnit.unit))
end
@@ -326,23 +329,23 @@ function br.getUnitsInRect(width,length, showLines, hp)
return unitCounter, UnitsInRect
end
-function br.getAngles(X1,Y1,Z1,X2,Y2,Z2)
+function healingEngineFunctions:getAngles(X1,Y1,Z1,X2,Y2,Z2)
return math.atan2(Y2-Y1,X2-X1)
end
-function br.getUnitsInCone(length,angle,hp)
- local playerX, playerY, playerZ = br.GetObjectPosition("player")
- local facing = br.GetObjectFacing("player")
+function healingEngineFunctions:getUnitsInCone(length,angle,hp)
+ local playerX, playerY, playerZ = br.functions.unit:GetObjectPosition("player")
+ local facing = br.functions.unit:GetObjectFacing("player")
local units = {};
- for i = 1, #br.friend do
- local thisUnit = br.friend[i].unit
+ for i = 1, #br.engines.healingEngine.friend do
+ local thisUnit = br.engines.healingEngine.friend[i].unit
if thisUnit.hp <= hp then
- if br.friend[i].distance <= length then
- if not br.GetUnitIsUnit(thisUnit,"player") and (br.isDummy(thisUnit) or br.GetUnitIsFriend(thisUnit,"player")) then
- local unitX, unitY, unitZ = br.GetObjectPosition(thisUnit)
+ if br.engines.healingEngine.friend[i].distance <= length then
+ if not br.functions.unit:GetUnitIsUnit(thisUnit,"player") and (br.functions.unit:isDummy(thisUnit) or br.functions.unit:GetUnitIsFriend(thisUnit,"player")) then
+ local unitX, unitY, unitZ = br.functions.unit:GetObjectPosition(thisUnit)
if playerX and unitX then
- local angleToUnit = br.getAngles(playerX,playerY,playerZ,unitX,unitY,unitZ)
+ local angleToUnit = br.engines.healingEngineFunctions:getAngles(playerX,playerY,playerZ,unitX,unitY,unitZ)
local angleDifference = facing > angleToUnit and facing - angleToUnit or angleToUnit - facing
local shortestAngle = angleDifference < math.pi and angleDifference or math.pi*2 - angleDifference
local finalAngle = shortestAngle/math.pi*180
@@ -357,27 +360,27 @@ function br.getUnitsInCone(length,angle,hp)
return units
end
-function br.healConeAround(numUnitsp, healthp, anglep, rangeInfrontp, rangeAroundp)
+function healingEngineFunctions:healConeAround(numUnitsp, healthp, anglep, rangeInfrontp, rangeAroundp)
local total = 0
local numUnits = tonumber(numUnitsp)
local health = tonumber(healthp)
local angle = tonumber(anglep)
local rinfront = tonumber(rangeInfrontp)
local raround = tonumber(rangeAroundp)
- local playerX, playerY, playerZ = br.GetObjectPosition('player')
- local facing = br.GetObjectFacing("player")
-
- for i=1,#br.friend do
- local thisUnit = br.friend[i].unit
- if br.friend[i].hp < health then
+ local playerX, playerY, playerZ = br.functions.unit:GetObjectPosition('player')
+ local facing = br.functions.unit:GetObjectFacing("player")
+
+ for i=1,#br.engines.healingEngine.friend do
+ local thisUnit = br.engines.healingEngine.friend[i].unit
+ if br.engines.healingEngine.friend[i].hp < health then
-- First check around us, light of dawn do heal 5 yards around
- if br.friend[i].distance < raround then
+ if br.engines.healingEngine.friend[i].distance < raround then
total = total +1
else --dont doubledipp so an else
- if br.friend[i].distance < rinfront then --only if they are in range
- local unitX, unitY, unitZ = br.GetObjectPosition(thisUnit)
+ if br.engines.healingEngine.friend[i].distance < rinfront then --only if they are in range
+ local unitX, unitY, unitZ = br.functions.unit:GetObjectPosition(thisUnit)
if playerX and unitX then
- local angleToUnit = br.getAngles(playerX,playerY,playerZ,unitX,unitY,unitZ)
+ local angleToUnit = br.engines.healingEngineFunctions:getAngles(playerX,playerY,playerZ,unitX,unitY,unitZ)
local angleDifference = facing > angleToUnit and facing - angleToUnit or angleToUnit - facing
local shortestAngle = angleDifference < math.pi and angleDifference or math.pi*2 - angleDifference
local finalAngle = shortestAngle/math.pi*180
@@ -400,9 +403,9 @@ end
--
-- number to calculate the factorial of
-- Returns factorial of the integer n
-function br.Factorial(n)
+function healingEngineFunctions:Factorial(n)
if n < 0 then return nil end
- if n >= 2 then return n * br.Factorial(n - 1) end
+ if n >= 2 then return n * br.engines.healingEngineFunctions:Factorial(n - 1) end
return 1
end
@@ -414,7 +417,7 @@ end
-- table of points to consider (the domain) Looks like: {{x1,y1,z1},{x2,y2,z2},{x3,y3,z3}...}
-- the minimum number of points to choose from the table for each combination
-- Returns a table containing all possible unique combinations
-function br.GetCombinations(points, choose)
+function healingEngineFunctions:GetCombinations(points, choose)
local function fOfN(n, src, got, all)
local function tableConcat(t1,t2)
local t3 = {}
@@ -457,7 +460,7 @@ end
-- point to check distance from
-- table of x,y,z points to check distances against
-- Returns the distance to the closest x,y,z in the table
-function br.GetDistanceToClosestNeighbor(p,points)
+function healingEngineFunctions:GetDistanceToClosestNeighbor(p,points)
local minDist = 999
if p ~= nil and p.x ~= nil and p.y ~= nil and p.z ~= nil then
for i=1,#points do
@@ -478,7 +481,7 @@ end
-- radius of the circle
-- List of points to check
-- Returns the number of points inside the circle
-function br.GetNumPointsInCircle(center,radius,points)
+function healingEngineFunctions:GetNumPointsInCircle(center,radius,points)
if points == nil or #points < 1 then return 0 end
local count = 0
if center ~= nil and center.x ~= nil and center.y ~= nil and center.z ~= nil then
@@ -498,7 +501,7 @@ end
--
-- table of points
-- Returns the centroid of the points
-function br.GetCentroidOfPoints(points)
+function healingEngineFunctions:GetCentroidOfPoints(points)
if points == nil then return nil end
if #points < 1 then return nil end
if #points == 1 then return points[1] end
@@ -547,22 +550,22 @@ end
-- location to cast at: {x,y,z}
-- ID of spell to ground cast
-- Returns if successfully tried to cast
-function br.castGroundAtLocation(loc, SpellID)
- br._G.CastSpellByName(br._G.GetSpellInfo(SpellID))
+function healingEngineFunctions:castGroundAtLocation(loc, SpellID)
+ br._G.CastSpellByName(br.api.wow.GetSpellInfo(SpellID))
--local mouselookup = IsMouseButtonDown(2)
--MouselookStop()
local px,py,pz = br._G.ObjectPosition("player")
local hit1x,hit1y,hit1z = br._G.TraceLine(loc.x, loc.y, loc.z+5, loc.x, loc.y, loc.z-5, 0x110) -- Raytrace correct z, Terrain and WMO hit
- if hit1z ~= nil and
- br._G.TraceLine(px, py, pz+2, loc.x, loc.y, loc.z+1, 0x100010) == nil and
+ if hit1z ~= nil and
+ br._G.TraceLine(px, py, pz+2, loc.x, loc.y, loc.z+1, 0x100010) == nil and
br._G.TraceLine(loc.x, loc.y, loc.z+4, loc.x, loc.y, loc.z, 0x1) == nil then -- Check z and LoS, ignore terrain and m2 colissions and check no m2 on hook location
if br._G.SpellIsTargeting() then
br._G.ClickPosition(loc.x,loc.y,hit1z)
end
--if mouselookup then MouselookStart() end
- if br._G.SpellIsTargeting() then
- br._G.SpellStopTargeting()
- br.addonDebug("Canceling Spell", true)
+ if br._G.SpellIsTargeting() then
+ br._G.SpellStopTargeting()
+ br.functions.misc:addonDebug("Canceling Spell", true)
end
return true
end
@@ -575,7 +578,7 @@ end
-- Minimum number of units that must be inside the circle
-- radius of the circle
-- Returns the center {x,y,z} of the circle if at least minTargets are within the circle, otherwise returns nil
-function br.getBestGroundCircleLocation(unitTable,minTargets,maxHealTargets,radius)
+function healingEngineFunctions:getBestGroundCircleLocation(unitTable,minTargets,maxHealTargets,radius)
if unitTable == nil then print("getBestGround: unitTable is nil") end
if minTargets == nil then print("getBestGround: minTargets is nil") end
if #unitTable < minTargets then return nil end
@@ -586,8 +589,8 @@ function br.getBestGroundCircleLocation(unitTable,minTargets,maxHealTargets,radi
local thisUnit = unitTable[i].unit
-- get the x,y,z position for this Unit
if type(thisUnit) == "string" then
- X1,Y1,Z1 = br.GetObjectPosition(thisUnit)
- -- if we provide it a table, take that object position(accepts br.enemy[i] or br.friend[i])
+ X1,Y1,Z1 = br.functions.unit:GetObjectPosition(thisUnit)
+ -- if we provide it a table, take that object position(accepts br.engines.enemiesEngine.enemy[i] or br.engines.healingEngine.friend[i])
elseif thisUnit and thisUnit.x ~= 0 then
X1,Y1,Z1 = thisUnit.x,thisUnit.y,thisUnit.z
end
@@ -602,19 +605,19 @@ function br.getBestGroundCircleLocation(unitTable,minTargets,maxHealTargets,radi
-- any unit that is more than 'radius*2' distance from every other unit can be excluded
local pointsInRange = {}
for i=1,#points do
- if br.GetDistanceToClosestNeighbor(points[i],points) <= 2 * radius then
+ if br.engines.healingEngineFunctions:GetDistanceToClosestNeighbor(points[i],points) <= 2 * radius then
br._G.tinsert(pointsInRange,points[i])
end
end
if #pointsInRange < minTargets then return nil end
-- check the remaining units. If they are all (or all but 1) inside the circle centered on the whole group, then we have the best solution
- local center = br.GetCentroidOfPoints(pointsInRange)
+ local center = br.engines.healingEngineFunctions:GetCentroidOfPoints(pointsInRange)
if center == nil then return nil end
- local numInside = br.GetNumPointsInCircle(center,radius,pointsInRange)
- X1,Y1,Z1 = br.GetObjectPosition("player")
+ local numInside = br.engines.healingEngineFunctions:GetNumPointsInCircle(center,radius,pointsInRange)
+ X1,Y1,Z1 = br.functions.unit:GetObjectPosition("player")
local X2,Y2,Z2 = center.x,center.y,center.z
local LoS = br._G.TraceLine(X1, Y1, Z1 + 2, X2, Y2, Z2 + 2, 0x10) == nil
- local distance = br.getDistanceToLocation("player",X2,Y2,Z2) <= 40
+ local distance = br.functions.range:getDistanceToLocation("player",X2,Y2,Z2) <= 40
if (numInside >= #points - 1) and (numInside >= minTargets) and LoS and distance then return center end
-- start with taking #pointsInRange, #pointsInRange-1 at a time
@@ -631,17 +634,17 @@ function br.getBestGroundCircleLocation(unitTable,minTargets,maxHealTargets,radi
groupSize = maxHealTargets
end
while groupSize >= minTargets do
- local allCombinations = br.GetCombinations(pointsInRange,groupSize)
+ local allCombinations = br.engines.healingEngineFunctions:GetCombinations(pointsInRange,groupSize)
for i=1, #allCombinations do
-- get the centroid of this list of points
- local c = br.GetCentroidOfPoints(allCombinations[i])
+ local c = br.engines.healingEngineFunctions:GetCentroidOfPoints(allCombinations[i])
if c == nil then return nil end
local tx,ty,tz = c.x,c.y,c.z
local LoS = br._G.TraceLine(X1, Y1, Z1 + 2, tx, ty, tz + 2, 0x10) == nil
- local tdistance = br.getDistanceToLocation("player",tx,ty,tz) <= 40
+ local tdistance = br.functions.range:getDistanceToLocation("player",tx,ty,tz) <= 40
if LoS and tdistance then
-- how many of the points are inside the circle?
- local n = br.GetNumPointsInCircle(c,radius,allCombinations[i])
+ local n = br.engines.healingEngineFunctions:GetNumPointsInCircle(c,radius,allCombinations[i])
-- if we got a whole group inside the circle, we can stop here
if n >= groupSize then
-- Logging.WriteDebug("GetBestCircleLocation(): Found Whole Group Solution for " + n.ToString() + " points");
@@ -660,5 +663,5 @@ function br.getBestGroundCircleLocation(unitTable,minTargets,maxHealTargets,radi
groupSize = groupSize - 1
end
-- return the geocenter of the best grouping
- return br.GetCentroidOfPoints(bestGroup)
+ return br.engines.healingEngineFunctions:GetCentroidOfPoints(bestGroup)
end
diff --git a/System/engines/Interrupts.lua b/System/engines/Interrupts.lua
index c24b6c8d46..93a79fa979 100644
--- a/System/engines/Interrupts.lua
+++ b/System/engines/Interrupts.lua
@@ -1,14 +1,17 @@
local _, br = ...
+br.engines.interrupts = br.engines.interrupts or {}
+local interrupts = br.engines.interrupts
+
-- if shouldStopCasting(12345) then
-function br.shouldStopCasting(Spell)
+function interrupts:shouldStopCasting(Spell)
-- if we are on a boss fight
- if br.GetUnitExists("boss1") then
+ if br.functions.unit:GetUnitExists("boss1") then
-- Locally casting informations
local Boss1Cast,Boss1CastEnd,PlayerCastEnd,StopCasting
local MySpellCastTime
-- Set Spell Cast Time
- if br._G.GetSpellInfo(Spell) ~= nil then
- MySpellCastTime = (br._G.GetTime()*1000) + select(4,br._G.GetSpellInfo(Spell))
+ if br.api.wow.GetSpellInfo(Spell) ~= nil then
+ MySpellCastTime = (br._G.GetTime()*1000) + select(4,br.api.wow.GetSpellInfo(Spell))
else
return false
end
@@ -42,19 +45,19 @@ function br.shouldStopCasting(Spell)
PlayerCastEnd = MySpellCastTime
end
for i = 1,#ShouldContinue do
- if br.UnitBuffID("player",ShouldContinue[i])
- and (select(6,br.UnitBuffID("player",ShouldContinue[i]))*1000)+50 > Boss1CastEnd then
- br.ChatOverlay("\124cFFFFFFFFStopper Safety Found")
+ if br.functions.aura:UnitBuffID("player",ShouldContinue[i])
+ and (select(6,br.functions.aura:UnitBuffID("player",ShouldContinue[i]))*1000)+50 > Boss1CastEnd then
+ br.ui.chatOverlay:Show("\124cFFFFFFFFStopper Safety Found")
return false
end
end
if not br._G.UnitCastingInfo("player") and not br._G.UnitChannelInfo("player") and MySpellCastTime and br.SetStopTime
and MySpellCastTime > Boss1CastEnd then
- br.ChatOverlay("\124cFFD93B3BStop for "..Boss1Cast)
+ br.ui.chatOverlay:Show("\124cFFD93B3BStop for "..Boss1Cast)
return true
end
for j = 1,#ShouldStop do
- if Boss1Cast == select(1,br._G.GetSpellInfo(ShouldStop[j])) then
+ if Boss1Cast == select(1,br.api.wow.GetSpellInfo(ShouldStop[j])) then
br.SetStopTime = Boss1CastEnd
if PlayerCastEnd ~= nil then
if Boss1CastEnd < PlayerCastEnd then
@@ -66,14 +69,14 @@ function br.shouldStopCasting(Spell)
return StopCasting
end
end
-function br.betterStopCasting(Spell)
- local spellCastLengt = select(4,br._G.GetSpellInfo(Spell)) or 0
+function interrupts:betterStopCasting(Spell)
+ local spellCastLengt = select(4,br.api.wow.GetSpellInfo(Spell)) or 0
local MySpellCastTime = (br._G.GetTime()*1000) + spellCastLengt
- if br.shouldStopTime and br.shouldStopTime <= MySpellCastTime and not br.canContinue() then
+ if br.shouldStopTime and br.shouldStopTime <= MySpellCastTime and not br.engines.interrupts:canContinue() then
return true
end
end
-br.stopCasting = {
+interrupts.stopCasting = {
shouldContinue = {
[1022] = "Melee" , -- Hand of Protection
[31821] = "All",-- Devotion
@@ -87,9 +90,9 @@ br.stopCasting = {
[19750] = "Spell" -- Flash heal(Test)
}
}
-function br.canContinue()
- for i = 1, #br.stopCasting.shouldContinue do
- if br.UnitBuffID("player", br.stopCasting.shouldContinue[i]) then
+function interrupts:canContinue()
+ for i = 1, #interrupts.stopCasting.shouldContinue do
+ if br.functions.aura:UnitBuffID("player", interrupts.stopCasting.shouldContinue[i]) then
return true
end
end
diff --git a/System/engines/InterruptsFrame.lua b/System/engines/InterruptsFrame.lua
index e4b5eb3647..db7e0b0b52 100644
--- a/System/engines/InterruptsFrame.lua
+++ b/System/engines/InterruptsFrame.lua
@@ -7,14 +7,14 @@
-- br.im = CreateFrame('Frame')
-- -- local the frame while we load it.
-- local im = br.im
--- im:RegisterEvent("PLAYER_REGEN_ENABLED") -- wipe when we get out of combat(its needed because br.enemy will also be cleared)
+-- im:RegisterEvent("PLAYER_REGEN_ENABLED") -- wipe when we get out of combat(its needed because br.engines.enemiesEngine.enemy will also be cleared)
-- im:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") -- pulse when combat log events occurs
-- local function spellCastListener(self,category,...)
--- -- if br.getOptionCheck("Interrupts Handler") then
+-- -- if br.functions.misc:getOptionCheck("Interrupts Handler") then
-- -- if event is a combatlog event
-- if category == "COMBAT_LOG_EVENT_UNFILTERED" then
-- -- prevent nils of bot not started
--- if br.enemy == nil then
+-- if br.engines.enemiesEngine.enemy == nil then
-- return false
-- end
-- -- commonly used locals inside listener
@@ -24,8 +24,8 @@
-- local unitType = select(1,strsplit("-", sourceGUID or ""))
-- -- make sure it is a spell cast
-- if event == "SPELL_CAST_START" then
--- -- refresh enemies with current br.enemy
--- im.enemy = br.enemy
+-- -- refresh enemies with current br.engines.enemiesEngine.enemy
+-- im.enemy = br.engines.enemiesEngine.enemy
-- -- manage cast
-- return im:manageCast(CombatLogGetCurrentEventInfo())
-- end
@@ -53,8 +53,8 @@
-- -- local that will be used inside function environment
-- local canCast = canCast
-- local castSpell = castSpell
--- local br.getOptionCheck = br.getOptionCheck
--- local br.getOptionValue = br.getOptionValue
+-- local br.functions.misc:getOptionCheck = br.functions.misc:getOptionCheck
+-- local br.functions.misc:getOptionValue = br.functions.misc:getOptionValue
-- local br.getDistance = br.getDistance
-- local br.getSpellCD = br.getSpellCD
-- local GetSpellInfo = GetSpellInfo
@@ -78,7 +78,7 @@
-- -- first make sure we will be able to cast the spell
-- if canCast(spell,false,false) == true and #casters > 0 then
-- -- ToDo if the user sets its selector to target, only interupt current target.
--- local selectedMode,selectedTargets = br.getOptionValue("Interrupts Handler"),{}
+-- local selectedMode,selectedTargets = br.functions.misc:getOptionValue("Interrupts Handler"),{}
-- if selectedMode == 1 then
-- selectedTargets = { "target" }
-- elseif selectedMode == 2 then
@@ -96,13 +96,13 @@
-- -- iterate casters and find best occurance to cast on
-- local bestAoEInterruptTarget,bestAoEInterruptUnits = casters[1].unit,0
-- for i = 1, #casters do
--- if br.GetObjectExists(casters[i].unit) then
+-- if br.functions.unit:GetObjectExists(casters[i].unit) then
-- local thisCaster = casters[i]
-- if selectedMode == 4 or isSelectedTarget(thisCaster.guid) then
-- -- make sure unit is in a cluster > targets
-- if thisCaster.castersAround > targets then
-- -- see if we are in range
--- if br.getDistance("player",thisCaster.unit) < allowedDistance then
+-- if br.functions.range:getDistance("player",thisCaster.unit) < allowedDistance then
-- if thisCaster.castersAround > bestAoEInterruptUnits then
-- bestAoEInterruptTarget = thisCaster.unit
-- bestAoEInterruptUnits = thisCaster.castersAround
@@ -112,7 +112,7 @@
-- end
-- end
-- end
--- if bestAoEInterruptUnits > targets and br.GetObjectExists(bestAoEInterruptTarget) then
+-- if bestAoEInterruptUnits > targets and br.functions.unit:GetObjectExists(bestAoEInterruptTarget) then
-- if castSpell(bestAoEInterruptTarget,spell,false,false) then
-- im:debug("Cast AoE Interrupt on "..bestAoEInterruptTarget.."("..bestAoEInterruptUnits..") with "..spell..".",true)
-- return true
@@ -126,7 +126,7 @@
-- -- first make sure we will be able to cast the spell
-- if canCast(spell,false,false) == true then
-- -- ToDo if the user sets its selector to target, only interupt current target.
--- selectedMode,selectedTargets = br.getOptionValue("Interrupts Handler"),{ }
+-- selectedMode,selectedTargets = br.functions.misc:getOptionValue("Interrupts Handler"),{ }
-- if selectedMode == 1 then
-- selectedTargets = { "target" }
-- elseif selectedMode == 2 then
@@ -140,16 +140,16 @@
-- allowedDistance = 5
-- end
-- for i = 1, #casters do
--- if br.GetObjectExists(casters[i].unit) then
+-- if br.functions.unit:GetObjectExists(casters[i].unit) then
-- local thisCaster = casters[i]
-- if selectedMode == 4 or isSelectedTarget(thisCaster.guid) then
-- -- make sure we can interupt this spell
-- if thisCaster.canInterupt == true then
-- -- see if the spell is about to be finished casting or is a channel
-- local castPercent = (thisCaster.castEnd - GetTime())/thisCaster.castLenght
--- if thisCaster.castType == "chan" or (br.getSpellCD(spell) < thisCaster.castEnd - GetTime()
+-- if thisCaster.castType == "chan" or (br.functions.spell:getSpellCD(spell) < thisCaster.castEnd - GetTime()
-- and castPercent < (100 - percent)/100)
--- and br.getDistance("player",thisCaster.unit) < allowedDistance then
+-- and br.functions.range:getDistance("player",thisCaster.unit) < allowedDistance then
-- if castSpell(thisCaster.unit,spell,false,false) then
-- im:debug("Cast Interrupt "..thisCaster.unit.." with "..spell.." at "..castPercent,true)
-- -- if developer need to keep this spell/unit combo
@@ -192,20 +192,20 @@
-- -- find if that unit/spell combination should be interrupted
-- -- Prepare GUID to be reused via UnitID
-- local br = im
--- local function br.GetObjectExists(Unit)
--- if EWT and br.GetObjectExists(Unit) == true then
+-- local function br.functions.unit:GetObjectExists(Unit)
+-- if EWT and br.functions.unit:GetObjectExists(Unit) == true then
-- return true
-- else
-- return false
-- end
-- end
--- if br.enemy ~= nil then
--- for k, v in pairs(br.enemy) do
--- if br.GetObjectExists(br.enemy[k].unit) then
--- if br.enemy[k] and br.enemy[k].unit and sourceGUID == br.enemy[k].guid then
--- local thisUnit = br.enemy[k]
+-- if br.engines.enemiesEngine.enemy ~= nil then
+-- for k, v in pairs(br.engines.enemiesEngine.enemy) do
+-- if br.functions.unit:GetObjectExists(br.engines.enemiesEngine.enemy[k].unit) then
+-- if br.engines.enemiesEngine.enemy[k] and br.engines.enemiesEngine.enemy[k].unit and sourceGUID == br.engines.enemiesEngine.enemy[k].guid then
+-- local thisUnit = br.engines.enemiesEngine.enemy[k]
-- -- gather our infos
--- if br.getOptionCheck("Only Known Units") and not isInteruptCandidate(thisUnit.unit, spellID) then
+-- if br.functions.misc:getOptionCheck("Only Known Units") and not isInteruptCandidate(thisUnit.unit, spellID) then
-- im:debug(sourceName.." started casting "..spellID.." but is not gonna be interrupt.")
-- return --exit since we have checkd only known units but is not on the list
-- else
@@ -235,7 +235,7 @@
-- castLenght = unitCastLenght,
-- castType = castOrChan,
-- -- usually this distance check should use lib range
--- distance = br.getDistance("player",thisUnit),
+-- distance = br.functions.range:getDistance("player",thisUnit),
-- guid = sourceGUID,
-- id = thisUnit.id,
-- shouldInterupt = candidate,
@@ -296,7 +296,7 @@
-- thisCaster = casters[j]
-- -- if more than 0.25 remains on unit cast and its in range we count it
-- if (thisCaster.castEnd - GetTime() > 0.25 or thisCaster.castType == "chan") and
--- br.getDistance(thatCaster,thisCaster) < radius then
+-- br.functions.range:getDistance(thatCaster,thisCaster) < radius then
-- enemyCastersAround = enemyCastersAround + 1
-- end
-- end
@@ -307,8 +307,8 @@
-- -- function that return frange from thisCaster to thatCaster using their stored positions
-- function getDistanceInt(thatCaster,thisCaster)
-- local thatCaster,thisCaster = thatCaster,thisCaster
--- if br.GetObjectExists(thatCaster.unit) and br.GetUnitIsVisible(thatCaster.unit) == true
--- and br.GetObjectExists(thisCaster.unit) and br.GetUnitIsVisible(thisCaster.unit) == true then
+-- if br.functions.unit:GetObjectExists(thatCaster.unit) and br.functions.unit:GetUnitIsVisible(thatCaster.unit) == true
+-- and br.functions.unit:GetObjectExists(thisCaster.unit) and br.functions.unit:GetUnitIsVisible(thisCaster.unit) == true then
-- local X1,Y1,Z1 = thatCaster.pos
-- local X2,Y2,Z2 = thisCaster.pos
-- return math.sqrt(((X2-X1)^2) + ((Y2-Y1)^2) + ((Z2-Z1)^2))
@@ -340,7 +340,7 @@
-- end
-- -- check if a unit is a casting candidate according to its unitID and its current spell cast
-- function isInteruptCandidate(Unit,SpellID)
--- local unitID = br.GetObjectID(Unit)
+-- local unitID = br.functions.unit:GetObjectID(Unit)
-- for i = 1,#interruptCandidates do
-- thisCandidate = interruptCandidates[i]
-- if thisCandidate.unitID == 0 or unitID == thisCandidate.unitID then
diff --git a/System/engines/LootEngine.lua b/System/engines/LootEngine.lua
new file mode 100644
index 0000000000..3dd769eeb1
--- /dev/null
+++ b/System/engines/LootEngine.lua
@@ -0,0 +1,433 @@
+local _, br = ...
+br.engines.lootEngine = br.engines.lootEngine or {}
+local lootEngine = br.engines.lootEngine
+
+-----------------------------------------Loot Engine--------------------------------------
+if not lootEngine.metaTable then
+ -- Localizing commonly used functions
+ local tinsert, tremove, GetTime = br._G.tinsert, br._G.tremove, br._G.GetTime
+ local UnitCastingInfo, UnitChannelInfo = br._G.UnitCastingInfo, br._G.UnitChannelInfo
+ local IsMounted, GetUnitSpeed = br._G.IsMounted, br._G.GetUnitSpeed
+ local UnitIsDeadOrGhost, CanLootUnit = br._G.UnitIsDeadOrGhost, br._G.CanLootUnit
+ local ClearTarget = br._G.ClearTarget
+ -- Note: InteractUnit is accessed via br._G dynamically to support late-loading unlockers
+
+ local MAX_LOOT_RETRIES = 3 -- Max real loot attempts per GUID before treating it as a unique/unavailable item
+
+ -- Note: lootable table is managed by EnemiesEngine (br.engines.enemiesEngine.lootable)
+ lootEngine.lootUnit = nil -- Current unit to loot
+ lootEngine.oldMessage = nil -- For debug message tracking
+ lootEngine.lastBagWarning = 0 -- Throttle bag full warnings
+ lootEngine.lootAttempts = {} -- Track loot attempts to avoid spam
+ lootEngine.lootRetryCount = {} -- Track per-GUID actual attempt counts
+ lootEngine.lootSkipList = {} -- GUIDs to permanently skip this session (unique/unavailable items)
+ lootEngine.isLooting = false -- Global looting state flag
+ lootEngine.lootWindowOpen = false -- Track if loot window is currently open
+ lootEngine.lootStartTime = 0 -- Track when looting started for timeout
+ lootEngine.metaTable = {} -- MetaTable for the engine
+ lootEngine.metaTable.__index = {
+ name = "Loot Engine",
+ author = "CuteOne",
+ }
+
+ setmetatable(lootEngine, lootEngine.metaTable)
+
+ -- Debug function
+ function lootEngine:debug(message, forceShow)
+ -- Check if loot debugging is enabled or forceShow is true
+ local showDebug = forceShow or (br.data and br.data.settings and br.data.settings[br.loader.selectedSpec]
+ and br.data.settings[br.loader.selectedSpec].toggles
+ and br.data.settings[br.loader.selectedSpec].toggles["isDebugging"])
+
+ if showDebug and message and self.oldMessage ~= message then
+ br.functions.misc:addonDebug(" " .. (math.floor(GetTime() * 1000) / 1000) .. " " .. message, true)
+ self.oldMessage = message
+ end
+ end
+
+ -- Check if available bag slots, return number of free slots
+ function lootEngine:emptySlots()
+ local openSlots = 0
+ for i = 0, 4 do -- Check each bag
+ local numBagSlots = br._G.C_Container.GetContainerNumSlots(i)
+ if numBagSlots > 0 then -- Only look for slots if bag present
+ openSlots = openSlots + select(1, br._G.C_Container.GetContainerNumFreeSlots(i))
+ end
+ end
+ return openSlots
+ end
+
+ -- Count lootable units and set the next loot target
+ function lootEngine:lootCount()
+ local lootCount = 0
+ self.lootUnit = nil
+ local closestUnit = nil
+ local closestDistance = 999
+
+ -- Use the lootable table from EnemiesEngine
+ local lootableTable = br.engines.enemiesEngine.lootable
+ for k, v in pairs(lootableTable) do
+ if v ~= nil and v.unit ~= nil and v.guid ~= nil then
+ local thisUnit = v.unit
+ local thisGUID = v.guid
+ -- Verify the unit still exists and has loot
+ if br.functions.unit:GetObjectExists(thisUnit) then
+ local hasLoot = CanLootUnit(thisGUID)
+ if hasLoot then
+ lootCount = lootCount + 1
+ -- Find the closest lootable unit instead of just the first
+ local distance = br.functions.range:getDistance(thisUnit)
+ if distance < closestDistance then
+ closestDistance = distance
+ closestUnit = thisUnit
+ end
+ else
+ -- No longer has loot, remove from table
+ lootableTable[k] = nil
+ end
+ else
+ -- Unit no longer exists, remove from table
+ lootableTable[k] = nil
+ end
+ end
+ end
+
+ -- Set the closest unit as the loot target
+ if closestUnit then
+ self.lootUnit = closestUnit
+ end
+
+ return lootCount
+ end
+
+ -- Main looting logic
+ function lootEngine:getLoot(lootUnit)
+ if not lootUnit then
+ self:debug("getLoot called with nil unit")
+ return
+ end
+
+ -- Verify unit still exists
+ if not br.functions.unit:GetObjectExists(lootUnit) then
+ self:debug("Loot unit no longer exists")
+ self.lootUnit = nil
+ return
+ end
+
+ -- Don't loot while in combat
+ if br.functions.misc:isInCombat("player") then
+ return
+ end
+
+ -- Reset isLooting flag if it's been stuck for more than 3 seconds
+ if self.isLooting and (GetTime() - self.lootStartTime) > 3 then
+ self:debug("Looting timeout - resetting looting state")
+ self.isLooting = false
+ self.lootStartTime = 0
+ end
+
+ -- Don't attempt to loot if we're already in the process of looting
+ if self.isLooting or self.lootWindowOpen then
+ return
+ end
+
+ local unitGUID = br._G.UnitGUID(lootUnit)
+ if not unitGUID then
+ self:debug("Could not get GUID for loot unit")
+ return
+ end
+
+ -- Verify unit still has loot
+ if not CanLootUnit(unitGUID) then
+ self:debug("Unit no longer has loot available")
+ -- Remove from lootable table
+ if br.engines.enemiesEngine.lootable[lootUnit] then
+ br.engines.enemiesEngine.lootable[lootUnit] = nil
+ end
+ self.lootUnit = nil
+ return
+ end
+
+ -- Throttle loot attempts on same unit
+ local now = GetTime()
+ if self.lootAttempts[unitGUID] and (now - self.lootAttempts[unitGUID]) < 2 then
+ return
+ end
+
+ -- Skip units that have hit the retry limit with bag space (likely unique/already-owned items)
+ if self.lootSkipList[unitGUID] then
+ self:debug("Skipping " .. (br._G.UnitName(lootUnit) or "unknown") .. " - on skip list (unique item?)")
+ if br.engines.enemiesEngine.lootable[lootUnit] then
+ br.engines.enemiesEngine.lootable[lootUnit] = nil
+ end
+ self.lootUnit = nil
+ return
+ end
+
+ -- If we have a unit to loot, check if it's time to loot
+ if br.debug.timer:useTimer("getLoot", br.functions.misc:getOptionValue("Auto Loot")) then
+ local distance = br.functions.range:getDistance(lootUnit)
+
+ -- Check distance and line of sight
+ if distance < 7 and br.functions.misc:getLineOfSight("player", lootUnit) then
+ self.lootAttempts[unitGUID] = now
+ self.lootRetryCount[unitGUID] = (self.lootRetryCount[unitGUID] or 0) + 1
+
+ -- If we've exhausted retries and bags have space, the item is likely unique or otherwise blocked
+ if self.lootRetryCount[unitGUID] > MAX_LOOT_RETRIES and self:emptySlots() > 0 then
+ self:debug("Retry limit reached for " .. (br._G.UnitName(lootUnit) or "unknown") .. " with bag space available - likely a unique item, skipping")
+ self.lootSkipList[unitGUID] = true
+ if br.engines.enemiesEngine.lootable[lootUnit] then
+ br.engines.enemiesEngine.lootable[lootUnit] = nil
+ end
+ self.lootUnit = nil
+ return
+ end
+
+ self:debug("Looting " .. br._G.UnitName(lootUnit) .. " at " .. math.floor(distance) .. " yards (attempt " .. self.lootRetryCount[unitGUID] .. "/" .. MAX_LOOT_RETRIES .. ")")
+
+ -- Use InteractUnit or ObjectInteract depending on what the unlocker provides
+ local interactFunc = br._G.InteractUnit or br._G.ObjectInteract
+ if interactFunc then
+ if br.functions.misc:pause(true) then return end
+ self.isLooting = true
+ self.lootStartTime = now
+ interactFunc(lootUnit)
+ else
+ self:debug("ERROR: No interact function available (InteractUnit or ObjectInteract)")
+ -- Try direct TargetUnit + Interact as fallback
+ if br._G.TargetUnit then
+ br._G.TargetUnit(lootUnit)
+ br._G.C_Timer.After(0.1, function()
+ if br._G.InteractTarget then
+ if br.functions.misc:pause(true) then return end
+ self.isLooting = true
+ self.lootStartTime = now
+ br._G.InteractTarget()
+ end
+ end)
+ end
+ end
+
+ -- Note: Manual looting is handled by LOOT_OPENED event
+ -- This ensures the loot frame is fully loaded before attempting to loot items
+ else
+ if distance >= 7 then
+ self:debug("Unit too far for looting: " .. math.floor(distance) .. " yards")
+ else
+ self:debug("No line of sight to loot unit")
+ end
+ end
+
+ -- Clean up after looting
+ if not br.functions.misc:isInCombat("player") and self.isLooting then
+ br._G.ClearTarget()
+ end
+
+ return
+ end
+ end
+
+ -- Auto-loot functionality (called from rotation loop)
+ function lootEngine:autoLoot()
+ if not br.functions.misc:getOptionCheck("Auto Loot") then return end
+
+ -- Only loot when out of combat or no enemies nearby
+ if br.functions.misc:isInCombat("player") then
+ if br.player and br.player.enemies and br.player.enemies.get then
+ local nearbyEnemies = br.player.enemies.get(10)
+ if nearbyEnemies and nearbyEnemies[1] ~= nil then
+ return
+ end
+ else
+ return -- Can't check enemies, don't loot in combat
+ end
+ end
+
+ -- Check if there are units to loot
+ local lootableCount = self:lootCount()
+ if lootableCount == 0 then
+ return
+ end
+
+ -- Check for bag space
+ local freeSlots = self:emptySlots()
+ if freeSlots == 0 then
+ local now = GetTime()
+ -- Only show warning once every 30 seconds
+ if (now - self.lastBagWarning) > 30 then
+ br.ui.chatOverlay:Show("Bags are full, nothing will be looted!")
+ self.lastBagWarning = now
+ end
+ return
+ end
+
+ -- Check various conditions before looting
+ if UnitCastingInfo("player") ~= nil or UnitChannelInfo("player") ~= nil then
+ return -- Don't interrupt casting
+ end
+
+ if IsMounted("player") then
+ return -- Don't loot while mounted
+ end
+
+ if GetUnitSpeed("player") > 0 then
+ return -- Don't loot while moving
+ end
+
+ if UnitIsDeadOrGhost("player") then
+ return -- Don't loot while dead
+ end
+
+ -- Use timer to throttle loot attempts
+ if not br.debug.timer:useTimer("lootTimer", 1) then
+ return
+ end
+
+ -- All checks passed, attempt to loot
+ if self.lootUnit then
+ self:getLoot(self.lootUnit)
+ else
+ self:debug("No valid loot unit found despite " .. lootableCount .. " lootable units")
+ end
+ end
+
+ -- Clean up stale loot attempts (prevent memory leak)
+ function lootEngine:cleanupAttempts()
+ local now = GetTime()
+ for guid, timestamp in pairs(self.lootAttempts) do
+ -- Remove attempts older than 30 seconds and reset their retry count
+ -- (allows re-attempting a GUID encountered again after a long absence)
+ if (now - timestamp) > 30 then
+ self.lootAttempts[guid] = nil
+ self.lootRetryCount[guid] = nil
+ -- Note: lootSkipList entries are intentionally kept for the session
+ end
+ end
+ end
+
+ -- Get statistics about current loot state
+ function lootEngine:getStats()
+ local stats = {
+ lootableCount = 0,
+ freeSlots = self:emptySlots(),
+ currentTarget = self.lootUnit,
+ attemptsTracked = 0,
+ }
+
+ -- Count lootable units
+ for k, _ in pairs(br.engines.enemiesEngine.lootable) do
+ if br.engines.enemiesEngine.lootable[k] ~= nil then
+ stats.lootableCount = stats.lootableCount + 1
+ end
+ end
+
+ -- Count tracked attempts
+ for _ in pairs(self.lootAttempts) do
+ stats.attemptsTracked = stats.attemptsTracked + 1
+ end
+
+ return stats
+ end
+
+ -- Handle manual looting when Auto Loot game option is disabled
+ function lootEngine:manuallyLootAll()
+ if not br._G.LootFrame:IsShown() then return end
+
+ local numItems = br._G.GetNumLootItems()
+ if numItems == 0 then return end
+
+ self:debug("Manually looting " .. numItems .. " items")
+
+ -- Loot all items
+ for slot = 1, numItems do
+ if br._G.LootSlotHasItem(slot) then
+ br._G.LootSlot(slot)
+ end
+ end
+
+ -- Close loot window
+ br._G.CloseLoot()
+ end
+
+ -- Handle Bind on Pickup confirmation
+ function lootEngine:confirmBindOnPickup(slot)
+ -- Get info about the item being looted
+ local itemLink = br._G.GetLootSlotLink(slot)
+ if itemLink then
+ self:debug("Auto-confirming BoP loot: " .. itemLink)
+ else
+ self:debug("Auto-confirming BoP loot at slot " .. slot)
+ end
+
+ -- Confirm the binding
+ br._G.ConfirmLootSlot(slot)
+
+ -- After confirming BoP, check if we need to manually loot remaining items
+ if br._G.GetCVar("AutoLootDefault") == "0" then
+ -- Small delay to allow the confirmation to process
+ br._G.C_Timer.After(0.1, function()
+ lootEngine:manuallyLootAll()
+ end)
+ end
+ end
+
+ -- Initialize the engine (called when addon loads)
+ function lootEngine:init()
+ self:debug("Loot Engine initialized")
+
+ -- Set up periodic cleanup
+ local cleanupFrame = br._G.CreateFrame("Frame")
+ cleanupFrame:SetScript("OnUpdate", function(self, elapsed)
+ self.timeSinceLastCleanup = (self.timeSinceLastCleanup or 0) + elapsed
+ if self.timeSinceLastCleanup >= 30 then -- Clean up every 30 seconds
+ lootEngine:cleanupAttempts()
+ self.timeSinceLastCleanup = 0
+ end
+ end)
+
+ -- Register for loot events
+ local eventFrame = br._G.CreateFrame("Frame")
+ eventFrame:RegisterEvent("LOOT_OPENED")
+ eventFrame:RegisterEvent("LOOT_CLOSED")
+ eventFrame:RegisterEvent("LOOT_BIND_CONFIRM")
+ eventFrame:SetScript("OnEvent", function(self, event, ...)
+ if event == "LOOT_OPENED" then
+ lootEngine.lootWindowOpen = true
+ lootEngine:debug("Loot window opened")
+
+ -- Remove the unit from lootable table now that loot window opened successfully
+ if lootEngine.lootUnit then
+ -- Find and remove the unit from the lootable table (indexed by unit token)
+ if br.engines.enemiesEngine.lootable[lootEngine.lootUnit] then
+ br.engines.enemiesEngine.lootable[lootEngine.lootUnit] = nil
+ lootEngine:debug("Removed " .. br._G.UnitName(lootEngine.lootUnit) .. " from lootable table")
+ end
+ end
+
+ -- If program Auto Loot is enabled and game Auto Loot is disabled, manually loot
+ if br.functions.misc:getOptionCheck("Auto Loot") and br._G.GetCVar("AutoLootDefault") == "0" then
+ -- Small delay to ensure loot frame is fully populated
+ br._G.C_Timer.After(0.1, function()
+ lootEngine:manuallyLootAll()
+ end)
+ end
+
+ elseif event == "LOOT_CLOSED" then
+ lootEngine.lootWindowOpen = false
+ lootEngine.isLooting = false
+ lootEngine.lootStartTime = 0
+ lootEngine.lootUnit = nil
+ lootEngine:debug("Loot window closed")
+
+ elseif event == "LOOT_BIND_CONFIRM" then
+ -- Auto-confirm Bind on Pickup items when program Auto Loot is enabled
+ local slot = ...
+ if slot and br.functions.misc:getOptionCheck("Auto Loot") then
+ lootEngine:debug("Auto-confirming BoP item at slot " .. slot)
+ lootEngine:confirmBindOnPickup(slot)
+ end
+ end
+ end)
+ end
+end
diff --git a/System/engines/LootEngineFunctions.lua b/System/engines/LootEngineFunctions.lua
new file mode 100644
index 0000000000..42f28b1d2a
--- /dev/null
+++ b/System/engines/LootEngineFunctions.lua
@@ -0,0 +1,117 @@
+local _, br = ...
+local lootEngine = br.engines.lootEngine
+
+-----------------------------------------Loot Engine Functions--------------------------------------
+
+-- Sell all grey quality items to vendor
+function lootEngine:sellGreys()
+ for bag = 0, 4 do
+ for slot = 1, br._G.C_Container.GetContainerNumSlots(bag) do
+ local item = br._G.C_Container.GetContainerItemLink(bag, slot)
+ if item then
+ -- Check if it's grey quality item
+ if string.find(item, br.ui.colors.quality.grey) ~= nil then
+ local containerItemInfo = br._G.C_Container.GetContainerItemInfo(bag, slot)
+ local greyPrice = select(11, br._G.GetItemInfo(item)) * containerItemInfo.stackCount
+ if greyPrice > 0 then
+ br._G.C_Container.PickupContainerItem(bag, slot)
+ br._G.PickupMerchantItem()
+ end
+ end
+ end
+ end
+ end
+ br.ui.chatOverlay:Show("Sold Greys.")
+end
+
+-- Delete lowest value grey items (keeps higher value greys)
+function lootEngine:dumpGreys(numToDump)
+ local greyTable = {}
+
+ -- Collect all grey items with their prices
+ for bag = 0, 4 do
+ for slot = 1, br._G.C_Container.GetContainerNumSlots(bag) do
+ local item = br._G.C_Container.GetContainerItemLink(bag, slot)
+ if item then
+ -- Check if it's grey quality item
+ if string.find(item, br.ui.colors.quality.grey) ~= nil then
+ local containerItemInfo = br._G.C_Container.GetContainerItemInfo(bag, slot)
+ local greyPrice = select(11, br._G.GetItemInfo(item)) * containerItemInfo.stackCount
+ if greyPrice > 0 then
+ br._G.tinsert(greyTable, {
+ Bag = bag,
+ Slot = slot,
+ Price = greyPrice,
+ Item = item
+ })
+ end
+ end
+ end
+ end
+ end
+
+ -- Sort by price (lowest first)
+ table.sort(greyTable, function(x, y)
+ if x.Price and y.Price then
+ return x.Price < y.Price
+ elseif x.Price then
+ return false
+ else
+ return true
+ end
+ end)
+
+ -- Delete the cheapest items
+ for i = 1, numToDump do
+ if greyTable[i] ~= nil then
+ br._G.C_Container.PickupContainerItem(greyTable[i].Bag, greyTable[i].Slot)
+ br._G.DeleteCursorItem()
+ br._G.print("|cffFF0000Removed Grey Item: " .. greyTable[i].Item)
+ end
+ end
+end
+
+-- Slash Commands Registration
+-- Sell Greys Commands
+SLASH_Greys1 = "/grey"
+SLASH_Greys2 = "/greys"
+function br._G.SlashCmdList.Greys(msg, editbox)
+ br.engines.lootEngine:sellGreys()
+end
+
+-- Dump Greys Commands
+SLASH_DumpGrey1 = "/dumpgreys"
+SLASH_DumpGrey2 = "/dg"
+function br._G.SlashCmdList.DumpGrey(msg, editbox)
+ br.engines.lootEngine:dumpGreys(1)
+end
+
+-- Loot Stats Command
+SLASH_LootStats1 = "/lootstats"
+SLASH_LootStats2 = "/ls"
+function br._G.SlashCmdList.LootStats(msg, editbox)
+ if not br.engines.lootEngine or not br.engines.lootEngine.getStats then
+ br._G.print("|cffFF0000Loot Engine not initialized!")
+ return
+ end
+
+ local stats = br.engines.lootEngine:getStats()
+ br._G.print("|cff00FF00=== Loot Engine Stats ===")
+ br._G.print("|cffFFFFFFLootable Units: " .. (stats.lootableCount or 0))
+ br._G.print("|cffFFFFFFFree Bag Slots: " .. (stats.freeSlots or 0))
+ br._G.print("|cffFFFFFFCurrent Target: " .. (stats.currentTarget or "None"))
+ br._G.print("|cffFFFFFFTracked Attempts: " .. (stats.attemptsTracked or 0))
+ br._G.print("|cffFFFFFFIs Looting: " .. tostring(br.engines.lootEngine.isLooting or false))
+ br._G.print("|cffFFFFFFLoot Window Open: " .. tostring(br.engines.lootEngine.lootWindowOpen or false))
+
+ -- List all lootable units
+ if stats.lootableCount > 0 then
+ br._G.print("|cff00FF00Lootable Units:")
+ for k, v in pairs(br.engines.enemiesEngine.lootable) do
+ if v and v.unit and v.name then
+ local distance = br.functions.range:getDistance(v.unit) or 999
+ br._G.print(" - " .. v.name .. " (" .. k .. ") at " .. math.floor(distance) .. " yards")
+ end
+ end
+ end
+end
diff --git a/System/engines/QuestTracker.lua b/System/engines/QuestTracker.lua
index 93868d9e99..a1e0109586 100644
--- a/System/engines/QuestTracker.lua
+++ b/System/engines/QuestTracker.lua
@@ -1,69 +1,222 @@
local _, br = ...
-- local LibDraw = br._G.LibStub("LibDraw-BR")
-br.QuestCache = {}
+br.engines.questTracker = br.engines.questTracker or {}
+local questTracker = br.engines.questTracker
+questTracker.cache = questTracker.cache or {}
+questTracker.objectCheckCache = questTracker.objectCheckCache or {} -- Cache for already-checked objects
+questTracker.unitResultCache = questTracker.unitResultCache or {} -- Cache for isQuestUnit results by GUID
+questTracker.objectResultCache = questTracker.objectResultCache or {} -- Cache for isQuestObject results by GUID
+questTracker.questObjectivesCache = questTracker.questObjectivesCache or {} -- Cache parsed quest objectives
+questTracker.lastCacheClean = questTracker.lastCacheClean or 0
+local CACHE_EXPIRATION_TIME = 5 -- Seconds before cached result expires
+local CACHE_CLEAN_INTERVAL = 30 -- Seconds between cache cleanups
--Quest stuff
--local questPlateTooltip = CreateFrame('GameTooltip', 'QuestPlateTooltip', nil, 'GameTooltipTemplate')
local questTooltipScanQuest = br._G.CreateFrame("GameTooltip", "QuestPlateTooltipScanQuest", nil, "GameTooltipTemplate")
local ScannedQuestTextCache = {}
-function br.isQuestUnit(Pointer)
+-- Clean expired cache entries to prevent memory bloat
+local function cleanExpiredCaches()
+ local currentTime = br._G.GetTime()
+ local cleanCount = 0
+
+ -- Clean unit result cache
+ for guid, data in pairs(questTracker.unitResultCache) do
+ if currentTime - data.timestamp > CACHE_EXPIRATION_TIME then
+ questTracker.unitResultCache[guid] = nil
+ cleanCount = cleanCount + 1
+ end
+ end
+
+ -- Clean object result cache
+ for guid, data in pairs(questTracker.objectResultCache) do
+ if currentTime - data.timestamp > CACHE_EXPIRATION_TIME then
+ questTracker.objectResultCache[guid] = nil
+ cleanCount = cleanCount + 1
+ end
+ end
+
+ questTracker.lastCacheClean = currentTime
+end
+
+-- Helper function to add name variations to cache (called during cache build)
+local function addNameVariations(name, cache)
+ if not name or #name < 3 then return end
+
+ -- Add the original name
+ cache[name] = true
+
+ -- Add plural variation (add 's')
+ cache[name .. "s"] = true
+
+ -- Add plural variation (add 'es')
+ cache[name .. "es"] = true
+
+ -- If name ends in 's', add singular
+ if name:sub(-1) == "s" then
+ cache[name:sub(1, -2)] = true
+ end
+
+ -- If name ends in 'es', add singular
+ if name:sub(-2) == "es" then
+ cache[name:sub(1, -3)] = true
+ end
+
+ -- Handle common word form variations
+ -- "Salvaged" <-> "Salvageable"
+ if name:match("Salvaged") then
+ cache[name:gsub("Salvaged", "Salvageable")] = true
+ elseif name:match("Salvageable") then
+ cache[name:gsub("Salvageable", "Salvaged")] = true
+ end
+
+ -- "Damaged" <-> "Damageable"
+ if name:match("Damaged") then
+ cache[name:gsub("Damaged", "Damageable")] = true
+ elseif name:match("Damageable") then
+ cache[name:gsub("Damageable", "Damaged")] = true
+ end
+
+ -- Handle past tense variations (ed/able endings)
+ -- This catches more generic cases like "Broken" <-> "Breakable"
+ local root = name:match("^(.+)ed%s")
+ if root then
+ -- Found "ed " pattern (e.g., "Salvaged Metal")
+ cache[root .. "able " .. name:match("%s(.+)$")] = true
+ end
+
+ root = name:match("^(.+)able%s")
+ if root then
+ -- Found "able " pattern (e.g., "Salvageable Metal")
+ cache[root .. "ed " .. name:match("%s(.+)$")] = true
+ end
+end
+
+function questTracker:isQuestUnit(Pointer)
+ -- Get GUID for caching
local guid
if not br._G["lb"] then
guid = br._G.UnitGUID(Pointer)
else
guid = Pointer
end
- --local myName = UnitName("player")
- if guid then
- questTooltipScanQuest:SetOwner(br._G["WorldFrame"], 'ANCHOR_NONE')
- questTooltipScanQuest:SetHyperlink('unit:' .. guid)
- for i = 1, questTooltipScanQuest:NumLines() do
- ScannedQuestTextCache[i] = br._G["QuestPlateTooltipScanQuestTextLeft" .. i]
+
+ -- Check GUID-based result cache first
+ if guid and questTracker.unitResultCache[guid] then
+ local cached = questTracker.unitResultCache[guid]
+ if br._G.GetTime() - cached.timestamp < CACHE_EXPIRATION_TIME then
+ return cached.result
+ end
+ end
+
+ -- Periodic cache cleanup
+ if br._G.GetTime() - questTracker.lastCacheClean > CACHE_CLEAN_INTERVAL then
+ cleanExpiredCaches()
+ end
+
+ -- First check: Use native API if available (most reliable)
+ if br._G.UnitIsQuestBoss then
+ local isQuestBoss = br._G.UnitIsQuestBoss(Pointer)
+ if isQuestBoss then
+ -- Double-check it's not dead (unless friendly, like rescuable NPCs)
+ if not br.functions.unit:GetUnitIsDeadOrGhost(Pointer) or br.functions.unit:GetUnitIsFriend("player", Pointer) then
+ if guid then
+ questTracker.unitResultCache[guid] = {result = true, timestamp = br._G.GetTime()}
+ end
+ return true
+ end
+ end
+ end
+
+ -- Second check: Name-based matching (similar to object checking)
+ -- Check if the unit's name appears in any active quest objective
+ local unitName = br._G.UnitName(Pointer)
+ if unitName and (not br.functions.unit:GetUnitIsDeadOrGhost(Pointer) or br.functions.unit:GetUnitIsFriend("player", Pointer)) then
+ local lowerUnitName = unitName:lower()
+ -- Use cached objectives instead of repeated API calls
+ for _, objectives in pairs(questTracker.questObjectivesCache) do
+ for _, objText in ipairs(objectives) do
+ if objText:find(lowerUnitName, 1, true) then
+ if guid then
+ questTracker.unitResultCache[guid] = {result = true, timestamp = br._G.GetTime()}
+ end
+ return true
+ end
+ end
end
end
+
+ -- Third check: Tooltip scanning for drop quests (expensive, use sparingly)
+ -- Some mobs drop quest items but aren't named in objectives (e.g., "Zan'thik Shackles" dropped by various Zan'thik mobs)
+ -- Their tooltips show quest info to indicate they can drop quest items
+ -- Only do this check if we haven't found a match yet and GUID exists
+ if not guid then
+ return false
+ end
+
+ -- Perform tooltip scan (expensive operation)
+ questTooltipScanQuest:SetOwner(br._G["WorldFrame"], 'ANCHOR_NONE')
+ questTooltipScanQuest:SetHyperlink('unit:' .. guid)
+ local numLines = questTooltipScanQuest:NumLines()
+
+ -- Early exit if no lines
+ if numLines == 0 then
+ questTracker.unitResultCache[guid] = {result = false, timestamp = br._G.GetTime()}
+ return false
+ end
+
+ -- Cache tooltip lines
+ for i = 1, numLines do
+ ScannedQuestTextCache[i] = br._G["QuestPlateTooltipScanQuestTextLeft" .. i]
+ end
+
local isQuestUnit = false
local atLeastOneQuestUnfinished = false
for i = 1, #ScannedQuestTextCache do
local text = ScannedQuestTextCache[i]:GetText()
- if (br.QuestCache[text]) then
- --unit belongs to a quest
- isQuestUnit = true
- -- local amount1, amount2 = nil, nil
- local j = i
- while (ScannedQuestTextCache[j + 1]) do
- --check if the unit objective isn't already done
- local nextLineText = ScannedQuestTextCache[j + 1]:GetText()
- if (nextLineText) then
- if not nextLineText:match(br._G["THREAT_TOOLTIP"]) then
- local p1, p2 = nextLineText:match("(%d+)/(%d+)")
- if (not p1) then
- -- check for % based quests
- p1 = nextLineText:match("(%d+%%)")
- if p1 then
- -- remove the % sign for consistency
- p1 = string.gsub(p1, "%%", '')
+ if text then
+ -- Check if tooltip line matches cached quest text
+ if br.engines.questTracker.cache[text] then
+ isQuestUnit = true
+ -- Check the following lines for quest progress
+ local j = i
+ while ScannedQuestTextCache[j + 1] do
+ local nextLineText = ScannedQuestTextCache[j + 1]:GetText()
+ if nextLineText then
+ if not nextLineText:match(br._G["THREAT_TOOLTIP"]) then
+ local p1, p2 = nextLineText:match("(%d+)/(%d+)")
+ if not p1 then
+ -- Check for % based quests
+ p1 = nextLineText:match("(%d+%%)")
+ if p1 then
+ p1 = string.gsub(p1, "%%", '')
+ end
end
+ if (p1 and p2 and not (p1 == p2)) or (p1 and not p2 and not (p1 == "100")) then
+ -- Quest not completed
+ atLeastOneQuestUnfinished = true
+ break -- Early exit once we found unfinished quest
+ end
+ else
+ break -- Break here, saw threat% -> quest text is done
end
- if (p1 and p2 and not (p1 == p2)) or (p1 and not p2 and not (p1 == "100")) then
- -- quest not completed
- atLeastOneQuestUnfinished = true
- -- amount1, amount2 = p1, p2
- end
- else
- j = 99 --safely break here, as we saw threat% -> quest text is done
end
+ j = j + 1
+ end
+ if atLeastOneQuestUnfinished then
+ break -- Early exit from outer loop
end
- j = j + 1
end
end
end
- if isQuestUnit and atLeastOneQuestUnfinished and
- (not br.GetUnitIsDeadOrGhost(Pointer) or br.GetUnitIsFriend("player", Pointer)) then
- return true
- else
- return false
- end
+
+ local result = isQuestUnit and atLeastOneQuestUnfinished and
+ (not br.functions.unit:GetUnitIsDeadOrGhost(Pointer) or br.functions.unit:GetUnitIsFriend("player", Pointer))
+
+ -- Cache the result
+ questTracker.unitResultCache[guid] = {result = result, timestamp = br._G.GetTime()}
+ return result
end
local QuestCacheUpdate = function()
@@ -75,8 +228,10 @@ local QuestCacheUpdate = function()
[57008] = true, -- Assault The Warring Clans
[57728] = true, --- Assault: The Endless Swarm
}
- --clear the quest cache
- br._G.wipe(br.QuestCache)
+ --clear the quest cache and object check cache
+ br._G.wipe(br.engines.questTracker.cache)
+ br._G.wipe(br.engines.questTracker.objectCheckCache)
+ br._G.wipe(br.engines.questTracker.questObjectivesCache)
--do not update if is inside an instance
local isInInstance = br._G.IsInInstance()
@@ -85,27 +240,90 @@ local QuestCacheUpdate = function()
end
--update the quest cache
- local numEntries = br._G.C_QuestLog.GetNumQuestLogEntries()
+ local numEntries = br._G.GetNumQuestLogEntries()
for questIdx = 1, numEntries do
-- local title, level, suggestedGroup, isHeader, isCollapsed, isComplete, frequency, questId, startEvent, displayQuestID, isOnMap, hasLocalPOI, isTask, isStory = GetQuestLogTitle (questId)
- local questInfo = br._G.C_QuestLog.GetInfo(questIdx)
- local title = questInfo["title"]
- local questId = questInfo["questID"]
+ -- local questInfo = br._G.C_QuestLog.GetInfo(questIdx)
+ local title, _, _, _, _, _, _, questId = br._G.GetQuestLogTitle(questIdx)
+ -- local title = questInfo["title"]
+ -- local questId = questInfo["questID"]
if (type(questId) == "number" and questId > 0 and ignoreQuest[questId] == nil) then -- and not isComplete
- br.QuestCache[title] = true
+ -- Cache the quest title with variations
+ addNameVariations(title, br.engines.questTracker.cache)
+
+ -- Also cache quest objectives (units/objects to kill/collect)
+ local numObjectives = br._G.GetNumQuestLeaderBoards(questIdx)
+ if numObjectives and numObjectives > 0 then
+ local questObjectives = {}
+ for objIdx = 1, numObjectives do
+ local objectiveText, objectiveType, finished = br._G.GetQuestLogLeaderBoard(objIdx, questIdx)
+ if objectiveText and not finished then
+ -- Extract the objective name - just get everything before the progress numbers
+ -- Examples: "Kill Murlocs: 5/10" -> "Kill Murlocs"
+ -- "Dreadspore Bulbs destroyed 0/15" -> "Dreadspore Bulbs"
+ -- "Mantid slain: 0/200" -> "Mantid"
+
+ -- Match everything before number/number or number% patterns
+ local objectiveName = objectiveText:match("^(.-)%s*%d+/%d+") or objectiveText:match("^(.-)%s*%d+%%")
+
+ if objectiveName then
+ -- Clean up any trailing colons, action words, and whitespace
+ objectiveName = br._G.strtrim(objectiveName)
+ -- Remove trailing colon if present
+ objectiveName = objectiveName:gsub(":$", "")
+ -- Remove common action words at the end
+ objectiveName = objectiveName:gsub("%s+(slain|destroyed|collected|gathered|killed|used)$", "")
+ objectiveName = br._G.strtrim(objectiveName)
+
+ -- Cache with variations (handles plural/singular automatically)
+ addNameVariations(objectiveName, br.engines.questTracker.cache)
+
+ -- Store in structured objectives cache (lowercase for fast matching)
+ table.insert(questObjectives, objectiveText:lower())
+ end
+ end
+ end
+ -- Store objectives for this quest
+ if #questObjectives > 0 then
+ br.engines.questTracker.questObjectivesCache[questId] = questObjectives
+ end
+ end
end
end
local mapId = br._G.C_Map.GetBestMapForUnit("player")
if (mapId) then
- local worldQuests = br._G.C_TaskQuest.GetQuestsForPlayerByMapID(mapId)
+ -- Try new API first, fallback to old API for compatibility
+ local worldQuests = br._G.C_TaskQuest.GetQuestsForPlayerOnMap and
+ br._G.C_TaskQuest.GetQuestsForPlayerOnMap(mapId) or
+ br._G.C_TaskQuest.GetQuestsOnMap(mapId)
if (type(worldQuests) == "table") then
for _, questTable in ipairs(worldQuests) do
local questId = questTable.questId
if (type(questId) == "number" and questId > 0 and ignoreQuest[questId] == nil) then
local questName = br._G.C_TaskQuest.GetQuestInfoByQuestID(questId)
+ -- Remove debug print to avoid spam
+ -- br._G.print("Quest Name: " .. tostring(questName))
if (questName) then
- br.QuestCache[questName] = true
+ addNameVariations(questName, br.engines.questTracker.cache)
+ end
+
+ -- Try to get world quest objectives
+ local questInfo = br._G.C_TaskQuest.GetQuestInfoByQuestID(questId)
+ if questInfo then
+ -- World quests might have objectives we can parse similarly
+ local objectives = br._G.C_QuestLog.GetQuestObjectives(questId)
+ if objectives then
+ for _, objective in ipairs(objectives) do
+ if objective.text and not objective.finished then
+ local objectiveName = objective.text:match("^(.-):%s*%d") or objective.text:match("^(.-)%s+slain:%s*%d")
+ if objectiveName then
+ objectiveName = br._G.strtrim(objectiveName)
+ br.engines.questTracker.cache[objectiveName] = true
+ end
+ end
+ end
+ end
end
end
end
@@ -114,13 +332,25 @@ local QuestCacheUpdate = function()
end
local function FunctionQuestLogUpdate() --private
- if (br.QuestCacheThrottle and not br.QuestCacheThrottle["_cancelled"]) then
- br.QuestCacheThrottle:Cancel()
+ if (br.engines.questTracker.cacheThrottle and not br.engines.questTracker.cacheThrottle["_cancelled"]) then
+ br.engines.questTracker.cacheThrottle:Cancel()
end
- br.QuestCacheThrottle = br._G.C_Timer.NewTimer(2, QuestCacheUpdate)
+ -- Increased from 2 to 3 seconds to reduce CPU usage during quest spam
+ br.engines.questTracker.cacheThrottle = br._G.C_Timer.NewTimer(3, QuestCacheUpdate)
end
-function br.isQuestObject(object) --Ty Ssateneth
+-- Debug function to print cache contents
+function questTracker:printCache()
+ br._G.print("=== Quest Tracker Cache Contents ===")
+ local count = 0
+ for name, _ in pairs(br.engines.questTracker.cache) do
+ count = count + 1
+ br._G.print(count .. ". " .. tostring(name))
+ end
+ br._G.print("=== Total cached items: " .. count .. " ===")
+end
+
+function questTracker:isQuestObject(object) --Ty Ssateneth
local objectID = br._G.ObjectID(object)
local ignoreObjects = {
[327571] = true,
@@ -128,6 +358,63 @@ function br.isQuestObject(object) --Ty Ssateneth
if ignoreObjects[objectID] ~= nil then
return false
end
+
+ -- Get object GUID for caching
+ -- Object can be either a GUID string or an object pointer
+ local guid
+ if type(object) == "string" and object:match("^GameObject") then
+ guid = object
+ end
+
+ -- Check GUID-based result cache first
+ if guid and questTracker.objectResultCache[guid] then
+ local cached = questTracker.objectResultCache[guid]
+ if br._G.GetTime() - cached.timestamp < CACHE_EXPIRATION_TIME then
+ return cached.result
+ end
+ end
+
+ -- Get object name
+ local objectName = br._G.ObjectName(object)
+ if not objectName then
+ if guid then
+ questTracker.objectResultCache[guid] = {result = false, timestamp = br._G.GetTime()}
+ end
+ return false
+ end
+
+ -- Exclude common non-quest interactables
+ local nonQuestObjects = {
+ ["Mailbox"] = true,
+ ["Forge"] = true,
+ ["Anvil"] = true,
+ ["Cooking Fire"] = true,
+ ["Campfire"] = true,
+ ["Portal"] = true,
+ ["Meeting Stone"] = true,
+ ["Summoning Stone"] = true,
+ ["Bank"] = true,
+ ["Auction House"] = true,
+ ["Auctioneer"] = true,
+ }
+
+ if nonQuestObjects[objectName] then
+ if guid then
+ questTracker.objectResultCache[guid] = {result = false, timestamp = br._G.GetTime()}
+ end
+ return false
+ end
+
+ -- Check name-based cache first (avoids repeated lookups for same object name)
+ if br.engines.questTracker.objectCheckCache[objectName] ~= nil then
+ local result = br.engines.questTracker.objectCheckCache[objectName]
+ if guid then
+ questTracker.objectResultCache[guid] = {result = result, timestamp = br._G.GetTime()}
+ end
+ return result
+ end
+
+ -- Check hardcoded quest object IDs (specific expansions)
if objectID == 325958 or objectID == 325962 or objectID == 325963 or objectID == 325959 or objectID == 335703 or
objectID == 152692 or objectID == 163757 or objectID == 290542 or objectID == 113768 or objectID == 113771 or
objectID == 113769 or objectID == 113770 or objectID == 153290 or
@@ -140,11 +427,38 @@ function br.isQuestObject(object) --Ty Ssateneth
objectID == 325664 or objectID == 325665 or objectID == 325666 or objectID == 325667 or objectID == 325668 or
-- mechagon chests
objectID == 151166 -- algan units
- then return true end
- local glow = br.getItemGlow(object) --or select(2, CanLootUnit(object)) or questObj == 12
- if glow then
+ then
+ br.engines.questTracker.objectCheckCache[objectName] = true
+ if guid then
+ questTracker.objectResultCache[guid] = {result = true, timestamp = br._G.GetTime()}
+ end
return true
end
+
+ -- Main check: See if any quest objective text contains this object's name (or vice versa)
+ -- Check both directions to handle cases like "Whitepetal Reed" (quest) vs "Whitepetal Reeds" (object)
+ local lowerObjectName = objectName:lower()
+
+ -- Use cached objectives instead of repeated API calls
+ for _, objectives in pairs(questTracker.questObjectivesCache) do
+ for _, objText in ipairs(objectives) do
+ -- objText is already lowercase from cache building
+ -- Check if either string contains the other (handles singular/plural automatically)
+ if objText:find(lowerObjectName, 1, true) or lowerObjectName:find(objText, 1, true) then
+ br.engines.questTracker.objectCheckCache[objectName] = true
+ if guid then
+ questTracker.objectResultCache[guid] = {result = true, timestamp = br._G.GetTime()}
+ end
+ return true
+ end
+ end
+ end
+
+ -- Cache the negative result (object exists but isn't a quest object)
+ br.engines.questTracker.objectCheckCache[objectName] = false
+ if guid then
+ questTracker.objectResultCache[guid] = {result = false, timestamp = br._G.GetTime()}
+ end
return false
end
diff --git a/System/engines/Tracker.lua b/System/engines/Tracker.lua
index a8b8ed8f25..82e7385baf 100644
--- a/System/engines/Tracker.lua
+++ b/System/engines/Tracker.lua
@@ -1,7 +1,20 @@
local _, br = ...
local LibDraw = br._G.LibStub("LibDraw-BR")
+br.engines.tracker = br.engines.tracker or {}
+local tracker = br.engines.tracker
local tracking = false
local interactTime
+local lastInteractTime = 0
+local nextInteractDelay = 0
+-- Performance tunables
+tracker._updateInterval = tracker._updateInterval or 0.08 -- seconds between redraws
+tracker._lastUpdate = tracker._lastUpdate or 0
+tracker._maxDrawDistance = tracker._maxDrawDistance or 200 -- max distance to draw objects
+tracker._cache = tracker._cache or {}
+tracker._moveThreshold = tracker._moveThreshold or 0.25 -- meters to consider movement
+tracker._lastCount = tracker._lastCount or 0
+tracker._customTerms = tracker._customTerms or {}
+tracker._customTermsKey = tracker._customTermsKey or nil
local function getCastTime(unit)
@@ -25,10 +38,13 @@ local function isInteracting(unit)
return interactTime > time
end
-local function trackObject(object, isUnit, name, objectid, objectguid, interact)
+local function trackObject(object, isUnit, name, objectid, objectguid, interact, pX, pY, pZ, xOb, yOb, zOb, distance, drawLines, showExtraInfo, autoInteractEnabled)
if not br._G.ObjectExists(object) then return end
- local xOb, yOb, zOb = br._G.ObjectPosition(object)
- local pX, pY, pZ = br._G.ObjectPosition("player")
+ -- Allow caller to pass player position to avoid repeated lookups
+ if not pX then pX, pY, pZ = br._G.ObjectPosition("player") end
+ if not xOb then
+ xOb, yOb, zOb = br._G.ObjectPosition(object)
+ end
if zOb == nil then zOb = pZ end
if zOb == nil then return end
if interact == nil then
@@ -36,7 +52,12 @@ local function trackObject(object, isUnit, name, objectid, objectguid, interact)
end
-- local playerDistance = br._G.GetDistanceBetweenPositions(pX, pY, pZ, xOb, yOb, zOb)
local zDifference = math.floor(zOb - pZ)
- if xOb ~= nil then --and playerDistance < 200 then
+ if xOb ~= nil then
+ -- Cull distant objects early to avoid expensive draw calls
+ if not distance then
+ distance = br._G.GetDistanceBetweenPositions(pX, pY, pZ, xOb, yOb, zOb)
+ end
+ if distance > tracker._maxDrawDistance then return end
if math.abs(zDifference) > 50 then
LibDraw.SetColor(255, 0, 0, 100)
else
@@ -46,18 +67,20 @@ local function trackObject(object, isUnit, name, objectid, objectguid, interact)
if not Cr or Cr == 0 then Cr = 1.5 end
LibDraw.Circle(xOb, yOb, zOb, Cr)
if isUnit then
- LibDraw.Arrow(xOb, yOb, zOb, br._G.UnitFacing(object) + math.pi * 2, Cr / 2)
+ LibDraw.Arrow(xOb, yOb, zOb, br._G.UnitFacing(object) --[[+ math.pi * 2]], Cr / 2)
else
- LibDraw.Arrow(xOb, yOb, zOb, br._G.UnitFacing("player") + math.pi * 2, Cr / 2)
+ -- LibDraw.Arrow(xOb, yOb, zOb, br._G.UnitFacing("player") --[[+ math.pi * 2]], Cr / 2)
end
-- if name == "" or name == "Unknown" then
-- name = isUnit and br._G.UnitName(object) or nil
-- end
- if br.isChecked("Display Extra Info") then
+ if showExtraInfo == nil then showExtraInfo = br.functions.misc:isChecked("Display Extra Info") end
+ if showExtraInfo then
name = name .. " [" .. objectid .. "] " .. "\n" .. objectguid .. " [ZDiff: " .. zDifference .. "]"
end
LibDraw.Text(name, "GameFontNormal", xOb, yOb, zOb + 3)
- if br.isChecked("Draw Lines to Tracked Objects") then
+ if drawLines == nil then drawLines = br.functions.misc:isChecked("Draw Lines to Tracked Objects") end
+ if drawLines then
if math.abs(zDifference) > 50 then
LibDraw.SetColor(255, 0, 0, 80)
else
@@ -67,12 +90,20 @@ local function trackObject(object, isUnit, name, objectid, objectguid, interact)
end
-- local hasLoot = br._G.CanLootUnit(objectguid)
-- local interacting = isInteracting("player")
- if br.isChecked("Auto Interact with Any Tracked Object") and interact and not br.player.inCombat
- and --[[playerDistance]] br._G.GetDistanceBetweenPositions(pX, pY, pZ, xOb, yOb, zOb) <= 7 and
- not br.isUnitCasting("player") and not br.isMoving("player")
- and (not isInteracting("player") or br._G.CanLootUnit(objectguid)) --and br.timer:useTimer("Interact Delay", 1.5)
+ if autoInteractEnabled == nil then autoInteractEnabled = br.functions.misc:isChecked("Auto Interact with Any Tracked Object") end
+ if autoInteractEnabled and interact and not br.player.inCombat and distance <= 7
+ and not br.functions.cast:isUnitCasting("player") and not br.functions.misc:isMoving("player")
+ and not isInteracting("player") -- Only interact if not already interacting
+ and (not isUnit or br._G.CheckInteractDistance(object, 3)) -- Check if object can be interacted with (3 = interact distance)
then
- br._G.ObjectInteract(object)
+ local currentTime = br._G.GetTime()
+ -- Human-like delay: only interact if enough time has passed since last interaction
+ if currentTime >= (lastInteractTime + nextInteractDelay) then
+ br._G.ObjectInteract(object)
+ lastInteractTime = currentTime
+ -- Set next delay with variance: 0.5 to 1.0 seconds (random human-like timing)
+ nextInteractDelay = 0.5 + (br._G.math.random() * 0.5)
+ end
end
tracking = true
end
@@ -83,84 +114,159 @@ br._G.string.trim = function(string)
return from > #string and "" or string:match(".*%S", from)
end
-br.tracking = {}
-function br.objectTracker()
- if br.isChecked("Enable Tracker") then
- LibDraw:clearCanvas()
- -- Custom Tracker
- if (br.isChecked("Custom Tracker") and br.getOptionValue("Custom Tracker") ~= "" and
- string.len(br.getOptionValue("Custom Tracker")) >= 3) or br.isChecked("Rare Tracker") or
- br.isChecked("Quest Tracker")
- then
- local object
- local objUnit
- local name
- local objectid
- local objectguid
- local interact
- local track
- for i = 1, #br.tracking do
- object = br.tracking[i].object
- objUnit = br.tracking[i].unit
- name = br.tracking[i].name
- objectid = br.tracking[i].id
- objectguid = br.tracking[i].guid
- interact = nil
- track = false
+br.engines.tracker.tracking = {}
+function tracker:objectTracker()
+ if br.functions.misc:isChecked("Enable Tracker") then
+ local now = br._G.GetTime()
+
+ -- Adaptive redraw interval: smoother when FPS is good, lighter when FPS is low.
+ local fps = (br.engines.enemiesEngine and br.engines.enemiesEngine.cachedFPS) or br._G.GetFramerate() or 60
+ local interval = tracker._updateInterval
+ if fps < 30 then
+ if interval < 0.15 then interval = 0.15 end
+ elseif fps < 45 then
+ if interval < 0.10 then interval = 0.10 end
+ elseif fps > 55 then
+ if interval > 0.05 then interval = 0.05 end
+ end
+
+ if now - (tracker._lastUpdate or 0) < interval then return end
+ tracker._lastUpdate = now
+ -- Cache commonly used objects; call methods with colon to preserve self
+ local misc = br.functions.misc
+ local showCustom = misc:isChecked("Custom Tracker")
+ local customValue = tostring(misc:getOptionValue("Custom Tracker") or "")
+ local showRare = misc:isChecked("Rare Tracker")
+ local showQuest = misc:isChecked("Quest Tracker")
+ local inCombatPlayer = misc:isInCombat("player")
+ local inInstance = br._G.IsInInstance()
+
+ -- Cache draw-related toggles once per update (avoid per-object option checks)
+ local drawLines = misc:isChecked("Draw Lines to Tracked Objects")
+ local showExtraInfo = misc:isChecked("Display Extra Info")
+ local autoInteract = misc:isChecked("Auto Interact with Any Tracked Object")
+
+ -- Pre-parse custom search terms once per update (avoid per-object string.gmatch/strupper)
+ if showCustom and customValue ~= "" and string.len(customValue) >= 3 then
+ if tracker._customTermsKey ~= customValue then
+ tracker._customTermsKey = customValue
+ br._G.wipe(tracker._customTerms)
+ for k in string.gmatch(customValue, "([^,]+)") do
+ local term = br._G.string.trim(k)
+ if term and string.len(term) >= 3 then
+ tracker._customTerms[#tracker._customTerms + 1] = br._G.strupper(term)
+ end
+ end
+ end
+ else
+ tracker._customTermsKey = nil
+ br._G.wipe(tracker._customTerms)
+ end
+
+ -- Only proceed if any tracker mode is active
+ if ((showCustom and #tracker._customTerms > 0) or showRare or showQuest) then
+ local pX, pY, pZ = br._G.ObjectPosition("player")
+ local t = br.engines.tracker.tracking
+ local n = #t
+ local newCache = tracker._scratchCache or {}
+ tracker._scratchCache = newCache
+ br._G.wipe(newCache)
+ local redrawNeeded = false
+
+ -- First pass: collect positions and decide if redraw is needed
+ for i = 1, n do
+ local entry = t[i]
+ local object = entry.object
+ local objUnit = entry.unit
+ local name = entry.name
+ local objectid = entry.id
+ local objectguid = entry.guid
+ local interact = nil
+ local track = false
if object and name and objectid and objectguid then
- if br.isChecked("Rare Tracker") and not track then
- if br._G.UnitClassification(object) == "rare" then
- name = "(r) " .. name
+ if showRare and not track then
+ local class = br._G.UnitClassification(object)
+ if class == "rare" then
track = true
interact = false
- end
- if br._G.UnitClassification(object) == "rareelite" then
- name = "(r*) " .. name
+ elseif class == "rareelite" then
track = true
interact = false
end
end
- if br.isChecked("Custom Tracker") and not track then
- for k in string.gmatch(tostring(br.getOptionValue("Custom Tracker")), "([^,]+)") do
- if string.len(br._G.string.trim(k)) >= 3 and
- br._G.strmatch(br._G.strupper(name), br._G.strupper(br._G.string.trim(k)))
- then
+ if showCustom and not track then
+ local upperName = br._G.strupper(name)
+ for idx = 1, #tracker._customTerms do
+ if upperName:find(tracker._customTerms[idx], 1, true) then
track = true
+ break
end
end
end
- if br.isChecked("Quest Tracker") and not br.isInCombat("player") and not br._G.IsInInstance() then
- local ignoreList = {
- [36756] = true, -- Dead Soldier (Azshara)
- [36922] = true, -- Wounded Soldier (Azshara)
- [159784] = true, -- Wastewander Laborer
- [159804] = true, -- Wastewander Tracker
- [159803] = true, -- Wastewander Warrior
- [162605] = true, -- Aqir Larva
- [156079] = true -- Blood Font
- }
- if (br.getOptionValue("Quest Tracker") == 1 or br.getOptionValue("Quest Tracker") == 3) and
- object ~= nil and
- objUnit and br.isQuestUnit(object) and not br._G.UnitIsTapDenied(object)
+ if showQuest and not inCombatPlayer and not inInstance then
+ if (misc:getOptionValue("Quest Tracker") == 1 or misc:getOptionValue("Quest Tracker") == 3)
+ and object ~= nil and objUnit and br.engines.questTracker:isQuestUnit(object) and not br._G.UnitIsTapDenied(object)
then
-
- if object and br.GetObjectExists(object) and (ignoreList[objectid] ~= nil or
- (select(2, br._G.CanLootUnit(br._G.UnitGUID(object))) and br.getItemGlow(object))) then
- track = true
- else
- interact = false
+ if object and br.functions.unit:GetObjectExists(object) then
+ interact = true
track = true
end
end
- if (br.getOptionValue("Quest Tracker") == 2 or br.getOptionValue("Quest Tracker") == 3)
- and not objUnit and br.isQuestObject(object)
+ if (misc:getOptionValue("Quest Tracker") == 2 or misc:getOptionValue("Quest Tracker") == 3)
+ and not objUnit and br.engines.questTracker:isQuestObject(object)
then
+ interact = true
track = true
end
end
- -- Track
- if track then trackObject(object, objUnit, name, objectid, objectguid, interact) end
+ if track then
+ -- get position once for movement detection and culling
+ if br._G.ObjectExists(object) then
+ local xOb, yOb, zOb = br._G.ObjectPosition(object)
+ if xOb then
+ local distance = br._G.GetDistanceBetweenPositions(pX, pY, pZ, xOb, yOb, zOb)
+ if distance <= tracker._maxDrawDistance then
+ local old = tracker._cache[object]
+ if not old then
+ redrawNeeded = true
+ else
+ local dx = old.x - xOb
+ local dy = old.y - yOb
+ local dz = old.z - zOb
+ if (dx * dx + dy * dy + dz * dz) >= (tracker._moveThreshold * tracker._moveThreshold) then
+ redrawNeeded = true
+ end
+ end
+
+ local cached = old or {}
+ cached.x, cached.y, cached.z = xOb, yOb, zOb
+ cached.entry = entry
+ cached.distance = distance
+ cached.interact = interact
+ newCache[object] = cached
+ end
+ end
+ end
+ end
+ end
+ end
+
+ -- If count changed, force redraw
+ if n ~= tracker._lastCount then redrawNeeded = true end
+
+ -- Second pass: redraw only if needed
+ if redrawNeeded then
+ LibDraw:clearCanvas()
+ for object, data in pairs(newCache) do
+ local e = data.entry
+ -- reuse existing trackObject drawing logic; pass cached coords/distance and per-update option flags
+ trackObject(object, e.unit, e.name, e.id, e.guid,
+ data.interact,
+ pX, pY, pZ, data.x, data.y, data.z, data.distance,
+ drawLines, showExtraInfo, autoInteract)
end
+ tracker._cache = newCache
+ tracker._lastCount = n
end
end
elseif tracking then
diff --git a/System/engines/ttdTable.lua b/System/engines/ttdTable.lua
index ea3be87f0e..be3c2fa5c2 100644
--- a/System/engines/ttdTable.lua
+++ b/System/engines/ttdTable.lua
@@ -1,6 +1,9 @@
local _, br = ...
+br.engines.ttdTable = br.engines.ttdTable or {}
+local ttdTable = br.engines.ttdTable
local enemyTable
-function br.TTDRefresh(hpLimit)
+
+function br.engines.ttdTable:TTDRefresh(hpLimit)
if not enemyTable then
enemyTable = {
units = {},
@@ -18,10 +21,11 @@ function br.TTDRefresh(hpLimit)
if hpLimit == nil then hpLimit = 0 end
- for k, _ in pairs(br.enemy) do
- local object = br.enemy[k].unit
+ for k, _ in pairs(br.engines.enemiesEngine.enemy) do
+ local object = br.engines.enemiesEngine.enemy[k].unit
if not units[object] then
units[object] = br._G.GetTime()
+ -- Baseline health is the unit's current health at first sighting
health[object] = br._G.UnitHealth(object)
dps[object] = 0
if ttd[object] == nil then
@@ -64,21 +68,21 @@ function br.TTDRefresh(hpLimit)
end
end
-function br.getTTD(unit, hp)
- if br.getOptionCheck("Enhanced Time to Die") then
- if unit == "target" and br.GetObjectExists("target") then unit = br._G.UnitTarget("player") end
- if br.unitSetup.cache[unit] ~= nil then
- br.unitSetup.cache[unit]:unitTtd(hp)
- return br.unitSetup.cache[unit].ttd
+function br.engines.ttdTable:getTTD(unit, hp)
+ if br.functions.misc:getOptionCheck("Enhanced Time to Die") then
+ if unit == "target" and br.functions.unit:GetObjectExists("target") then unit = br._G.UnitTarget("player") end
+ if br.engines.enemiesEngine.unitSetup.cache[unit] ~= nil then
+ br.engines.enemiesEngine.unitSetup.cache[unit]:unitTtd(hp)
+ return br.engines.enemiesEngine.unitSetup.cache[unit].ttd or -2
end
return -2
end
- if br.isDummy() then return 99 end
- br.TTDRefresh(hp)
+ if br.functions.unit:isDummy() then return 99 end
+ ttdTable:TTDRefresh(hp)
local thisUnit = unit
-- if thisUnit ~= nil then
-- if not string.find(thisUnit, "0x") or not type(thisUnit) == "number" then
- -- if br.GetObjectExists(thisUnit) and not br.GetUnitIsDeadOrGhost(thisUnit) and br.GetUnitIsVisible(thisUnit) then
+ -- if br.functions.unit:GetObjectExists(thisUnit) and not br.functions.unit:GetUnitIsDeadOrGhost(thisUnit) and br.functions.unit:GetUnitIsVisible(thisUnit) then
-- thisUnit = br._G.GetObjectWithGUID(br._G.UnitGUID(thisUnit))
-- else
-- return -2
diff --git a/System/readers/combatlog.lua b/System/readers/combatlog.lua
index 2d377807b7..ac6dd29adb 100644
--- a/System/readers/combatlog.lua
+++ b/System/readers/combatlog.lua
@@ -1,15 +1,19 @@
local _, br = ...
-br.class = select(3, br._G.UnitClass("player"))
-br.guid = br._G.UnitGUID("player")
+local class = select(3, br._G.UnitClass("player"))
+local guid = br._G.UnitGUID("player")
+local nextMH = nil
+local swingTimer = nil
-- specific reader location
-br.read = {}
-br.read.combatLog = {}
-br.read.debuffTracker = {}
-br.read.enraged = {}
-local cl = br.read
+br.readers = {}
+br.readers.combatLog = br.readers.combatLog or {}
+br.readers.combatLog.debuffTracker = {}
+br.readers.combatLog.enraged = {}
+local combatLog = br.readers.combatLog
+
+
-- will update the br.read.enraged list
-function br.read.enrageReader(...)
- if br.getOptionCheck("Enrages Handler") then
+function combatLog:enrageReader(...)
+ if br.functions.misc:getOptionCheck("Enrages Handler") then
local timeStamp,
param,
hideCaster,
@@ -54,46 +58,44 @@ function br.read.enrageReader(...)
end
end
-function br.read.combatLog()
+function combatLog:combatLog()
---------------------------
--[[ Combat Log Reader --]]
local frame = br._G.CreateFrame("Frame")
frame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
local function reader(self, event, ...)
- local cl = br.read
-- this reader intend to hold all the combatlog related stuff. this is gonna be used
-- with as few checks as possible per class/spec as in raiding environment we have already enough to check
-- pulse common stuff for all classes
- cl:common(...)
+ combatLog:common(...)
-- best way is to split per class so lets make a selector for it
- local class = br.class
if class == 1 then -- Warrior
- cl:Warrior(...)
+ combatLog:Warrior(...)
elseif class == 2 then -- Paladin
- cl:Paladin(...)
+ combatLog:Paladin(...)
elseif class == 3 then -- Hunter
- cl:Hunter(...)
+ combatLog:Hunter(...)
elseif class == 4 then -- Rogue
- cl:Rogue(...)
+ combatLog:Rogue(...)
elseif class == 5 then -- Priest
- cl:Priest(...)
+ combatLog:Priest(...)
elseif class == 6 then -- Deathknight
- cl:Deathknight(...)
+ combatLog:Deathknight(...)
elseif class == 7 then -- Shaman
- cl:Shaman(...)
+ combatLog:Shaman(...)
elseif class == 8 then -- Mage
- cl:Mage(...)
+ combatLog:Mage(...)
elseif class == 9 then -- Warlock
- cl:Warlock(...)
+ combatLog:Warlock(...)
elseif class == 10 then -- Monk
- cl:Monk(...)
+ combatLog:Monk(...)
elseif class == 11 then -- Druid
- cl:Druid(...)
+ combatLog:Druid(...)
elseif class == 12 then -- Demon Hunter
- cl:DemonHunter(...)
+ combatLog:DemonHunter(...)
elseif class == 13 then -- Evoker
- cl:Evoker(...)
+ combatLog:Evoker(...)
end
end
-- add event to the reader
@@ -101,8 +103,8 @@ function br.read.combatLog()
end
-- class functions(Alphabetically)
-function cl:common(...)
- br.read.enrageReader(...)
+function combatLog:common(...)
+ br.readers.combatLog:enrageReader(...)
local timeStamp,
param,
hideCaster,
@@ -118,48 +120,155 @@ function cl:common(...)
spellName,
_,
spellType = br._G.CombatLogGetCurrentEventInfo()
- br.guid = br._G.UnitGUID("player")
- -- Unit Dies - Remove from enemy tracking
+ guid = br._G.UnitGUID("player")
+ -- Unit Dies - flag the OM so that the next regular OM update cycle cleans up promptly.
+ -- Do NOT call updateOM/Update synchronously here: they scan up to 500 objects each and
+ -- are called for every UNIT_DIED in the log. In a raid wipe or big M+ pull, multiple
+ -- enemies die in the same frame, stacking full scans inside the event handler and pushing
+ -- total Lua time over Blizzard's script budget, which leads to the disconnect.
if param == "UNIT_DIED" and br.unlocked then
- br:updateOM()
- br.om:Update()
+ -- Remove the dead unit from all enemy tracking tables immediately so the rotation
+ -- never acts on it, but skip the expensive full-scan.
+ local deadUnit = br._G.GetObjectWithGUID(destination)
+ if deadUnit ~= nil then
+ br.engines.enemiesEngine.unitSetup.cache[deadUnit] = nil
+ if br.engines.enemiesEngine.units[deadUnit] ~= nil then
+ br.engines.enemiesEngine.units[deadUnit] = nil
+ end
+ if br.engines.enemiesEngine.enemy[deadUnit] ~= nil then
+ br.engines.enemiesEngine.enemy[deadUnit] = nil
+ end
+ if br.engines.enemiesEngine.damaged ~= nil and br.engines.enemiesEngine.damaged[deadUnit] ~= nil then
+ br.engines.enemiesEngine.damaged[deadUnit] = nil
+ end
+ end
end
--[[Combat Validation]]
if br.player ~= nil then
local inInstance, instanceType = br._G.IsInInstance()
- if br.damaged == nil then
- br.damaged = {}
+ if br.engines.enemiesEngine.damaged == nil then
+ br.engines.enemiesEngine.damaged = {}
end
- if
- (not inInstance or (instanceType ~= "pvp" and instanceType ~= "arena")) and destination ~= nil and
- (param == "SPELL_DAMAGE" or param == "SWING_DAMAGE")
+ -- Throttle damaged table cleanup to once per second (instead of every combat log event)
+ if br.engines.enemiesEngine.damagedCleanupTimer == nil then
+ br.engines.enemiesEngine.damagedCleanupTimer = 0
+ end
+ local currentTime = br._G.GetTime()
+ local shouldCleanup = (currentTime - br.engines.enemiesEngine.damagedCleanupTimer) >= 1.0
+
+ if (not inInstance or (instanceType ~= "pvp" and instanceType ~= "arena")) and destination ~= nil
+ and (param == "SPELL_DAMAGE" or param == "SWING_DAMAGE")
then
- local thisUnit = br._G.GetObjectWithGUID(destination)
- if br.damaged[thisUnit] == nil and br.units[thisUnit] ~= nil and br.enemy[thisUnit] == nil then
- for i = 1, #br.friend do
- if br._G.ObjectPointer(br.friend[i].unit) == br._G.GetObjectWithGUID(source) then
- if br.damaged[thisUnit] == nil then
- br.damaged[thisUnit] = thisUnit
- break
- end
+ -- Group member pointer set: rebuilt at most once per second to give O(1) lookups below.
+ -- Previously two separate O(n) loops over healingEngine.friend ran for every single
+ -- damage event in the log. In a 20-person raid that was up to 40 ObjectPointer() calls
+ -- per event, multiplied by hundreds of events per second during heavy AoE.
+ if combatLog._groupPointerSet == nil then combatLog._groupPointerSet = {} end
+ if combatLog._groupPointerSetTime == nil then combatLog._groupPointerSetTime = 0 end
+ if (currentTime - combatLog._groupPointerSetTime) >= 1.0 then
+ combatLog._groupPointerSetTime = currentTime
+ local set = {}
+ set[br._G.ObjectPointer("player")] = true
+ if br.functions.unit:GetUnitExists("pet") then
+ set[br._G.ObjectPointer("pet")] = true
+ end
+ for i = 1, #br.engines.healingEngine.friend do
+ local friendUnit = br.engines.healingEngine.friend[i].unit
+ if friendUnit then
+ local ptr = br._G.ObjectPointer(friendUnit)
+ if ptr then set[ptr] = true end
+ end
+ end
+ combatLog._groupPointerSet = set
+ end
+ local groupSet = combatLog._groupPointerSet
+
+ -- Track units being damaged by player/party/raid
+ local sourcePtr = br._G.GetObjectWithGUID(source)
+ local damageTarget = br._G.GetObjectWithGUID(destination)
+ if damageTarget ~= nil and groupSet[sourcePtr] then
+ if br._G.UnitCanAttack("player", damageTarget) then
+ -- Use unified unitSetup structure for damaged table
+ -- Check cache first - if unit already exists, use cached version
+ local damagedUnit = br.engines.enemiesEngine.unitSetup.cache[damageTarget]
+ if not damagedUnit then
+ damagedUnit = br.engines.enemiesEngine.unitSetup:new(damageTarget)
+ end
+ if damagedUnit then
+ damagedUnit.damageReason = "damaged_by_group"
+ damagedUnit.damageTimestamp = currentTime
+ br.engines.enemiesEngine.damaged[damageTarget] = damagedUnit
end
end
end
+
+ -- Track units attacking player/party/raid
+ local destPtr = br._G.GetObjectWithGUID(destination)
+ local attackSource = sourcePtr
+ if attackSource ~= nil and groupSet[destPtr] then
+ -- Use unified unitSetup structure for damaged table
+ local attackingUnit = br.engines.enemiesEngine.unitSetup.cache[attackSource]
+ if not attackingUnit then
+ attackingUnit = br.engines.enemiesEngine.unitSetup:new(attackSource)
+ end
+ if attackingUnit then
+ attackingUnit.damageReason = "attacking_group"
+ attackingUnit.damageTimestamp = currentTime
+ br.engines.enemiesEngine.damaged[attackSource] = attackingUnit
+ end
+ end
end
- for k, v in pairs(br.damaged) do
- if br.units[v] == nil or not br._G.UnitAffectingCombat("player") or br.GetUnitIsDeadOrGhost(v) then
- br.damaged[v] = nil
+
+ -- Throttled cleanup of damaged table (runs max once per second)
+ if shouldCleanup then
+ br.engines.enemiesEngine.damagedCleanupTimer = currentTime
+ -- More careful cleanup of damaged table
+ -- Only remove units that are truly invalid, not just because they haven't been validated yet
+ for k, v in pairs(br.engines.enemiesEngine.damaged) do
+ local shouldRemove = false
+ -- v is now a unitSetup object, access .unit directly
+ local unit = v.unit
+
+ -- Remove if unit is dead or ghost
+ if br.functions.unit:GetUnitIsDeadOrGhost(unit) then
+ shouldRemove = true
+ -- Remove if unit no longer exists
+ elseif not br.functions.unit:GetObjectExists(unit) then
+ shouldRemove = true
+ -- Remove if not visible
+ elseif not br.functions.unit:GetUnitIsVisible(unit) then
+ shouldRemove = true
+ -- Remove if player out of combat AND unit was added more than 10 seconds ago
+ elseif not br._G.UnitAffectingCombat("player") and v.damageTimestamp and (currentTime - v.damageTimestamp) > 10 then
+ shouldRemove = true
+ -- Remove if unit is now friendly (e.g., MC broken)
+ elseif not br._G.UnitCanAttack("player", unit) then
+ shouldRemove = true
+ -- Remove if unit successfully validated and added to enemy table (job done)
+ elseif br.engines.enemiesEngine.enemy[unit] ~= nil then
+ shouldRemove = true
+ end
+
+ if shouldRemove then
+ br.engines.enemiesEngine.damaged[k] = nil
+ -- Only clear cache if unit is not in other tables (prevents premature cleanup)
+ -- OM cleanup will handle cache removal for units still in use elsewhere
+ if br.engines.enemiesEngine.enemy[unit] == nil
+ and br.engines.enemiesEngine.units[unit] == nil then
+ br.engines.enemiesEngine.unitSetup.cache[unit] = nil
+ end
+ end
end
end
end
--In flight
- if source == br.guid and param == "SPELL_CAST_SUCCESS" and br.unlocked and destination ~= nil then --EasyWoWToolbox ~= nil then
- br.InFlight.Add(spell, destination)
+ if source == guid and param == "SPELL_CAST_SUCCESS" and br.unlocked and destination ~= nil then --EasyWoWToolbox ~= nil then
+ br.functions.InFlight:Add(spell, destination)
end
- -- br.tracker.handleEvent(...)
+ -- br.engines.auraTracker:handleEvent(...)
----------------
--[[Item locks]]
- if source == br.guid then
+ if source == guid then
-- TODO: OUTDATED - MoP
local DPSPotionsSet = {
[1] = { Buff = 105702, Item = 76093 }, -- Intel
@@ -171,10 +280,10 @@ function cl:common(...)
if spell == DPSPotionsSet[i].Buff then
br.potionUsed = br._G.GetTime()
if br._G.UnitAffectingCombat("player") then
- br.ChatOverlay("Potion Used, can reuse in 60 secs.")
+ br.ui.chatOverlay:Show("Potion Used, can reuse in 60 secs.")
br.potionReuse = false
else
- br.ChatOverlay("Potion Used, cannot reuse.")
+ br.ui.chatOverlay:Show("Potion Used, cannot reuse.")
br.potionReuse = true
end
end
@@ -187,26 +296,43 @@ function cl:common(...)
br.swingTimer = 0
end
if br.nextMH == nil then
- br.nextMH = br._G.GetTime() + br._G.UnitAttackSpeed("player")
+ br.nextMH = br._G.GetTime() + (br._G.UnitAttackSpeed("player") or 0)
end
- if param == "SWING_DAMAGE" and source == br.guid then
+
+ -- Initialize swing timer when Attack spell is cast (6603 = Attack in Classic/Retail)
+ if param == "SPELL_CAST_SUCCESS" and source == guid and spell == 6603 then
+ local speed = br._G.UnitAttackSpeed("player") or 0
+ if speed > 0 then
+ -- Start swing timer with initial delay (first swing takes full weapon speed)
+ br.lastMH = br._G.GetTime()
+ br.nextMH = br.lastMH + speed
+ end
+ end
+
+ -- Reset swing timer on both damage and misses (important for Classic)
+ if (param == "SWING_DAMAGE" or param == "SWING_MISSED") and source == guid then
br.swingTimer = 0
br.lastMH = br._G.GetTime()
- br.nextMH = br.lastMH + br._G.UnitAttackSpeed("player")
+ local speed = br._G.UnitAttackSpeed("player") or 0
+ if speed > 0 then
+ br.nextMH = br.lastMH + speed
+ end
end
- if br.swingTimer then
- if br.nextMH - br._G.GetTime() < 0 then
+ -- Update swing timer every event
+ if br.nextMH and br._G.GetTime() then
+ local remaining = br.nextMH - br._G.GetTime()
+ if remaining < 0 then
br.swingTimer = 0
else
- br.swingTimer = br.nextMH - br._G.GetTime()
+ br.swingTimer = remaining
end
end
-----------------------------------
--[[ Item Use Success Recorder ]]
if param == "SPELL_CAST_SUCCESS" then
if sourceName ~= nil then
- if br.isInCombat("player") and br.GetUnitIsUnit(sourceName, "player") then
+ if br.functions.misc:isInCombat("player") and br.functions.unit:GetUnitIsUnit(sourceName, "player") then
if br.usePot == nil then
br.usePot = true
end
@@ -221,13 +347,13 @@ function cl:common(...)
end
------------------
--[[Spell Queues]]
- if br.getOptionCheck("Queue Casting") then
+ if br.functions.misc:getOptionCheck("Queue Casting") then
-----------------
--[[ Cast Failed --> Queue]]
if param == "SPELL_CAST_FAILED" then
if sourceName ~= nil then
if
- br.isInCombat("player") and br.GetUnitIsUnit(sourceName, "player") and
+ br.functions.misc:isInCombat("player") and br.functions.unit:GetUnitIsUnit(sourceName, "player") and
not br._G.C_Spell.IsSpellPassive(spell) and
spell ~= br.botSpell and
not br.botCast and
@@ -236,7 +362,7 @@ function cl:common(...)
then
local notOnCD = true
if br ~= nil and br.player ~= nil then
- notOnCD = br.getSpellCD(spell) <= br.player.gcdMax
+ notOnCD = br.functions.spell:getSpellCD(spell) <= br.player.gcdMax
end
-- set destination
if destination == "" then
@@ -246,26 +372,26 @@ function cl:common(...)
end
if br.player ~= nil and #br.player.queue == 0 and notOnCD then
br._G.tinsert(br.player.queue, { id = spell, name = spellName, target = br.queueDest })
- if not br.isChecked("Mute Queue") then
+ if not br.functions.misc:isChecked("Mute Queue") then
br._G.print("Added |cFFFF0000" .. spellName .. "|r to the queue.")
end
elseif br.player ~= nil and #br.player.queue ~= 0 then
for i = 1, #br.player.queue do
if spell == br.player.queue[i].id then
br._G.tremove(br.player.queue, i)
- if not br.isChecked("Mute Queue") then
+ if not br.functions.misc:isChecked("Mute Queue") then
br._G.print("Removed |cFFFF0000" .. spellName .. "|r from the queue.")
end
break
elseif notOnCD then
br._G.tinsert(br.player.queue, { id = spell, name = spellName, target = br.queueDest })
- if not br.isChecked("Mute Queue") then
+ if not br.functions.misc:isChecked("Mute Queue") then
br._G.print("Added |cFFFF0000" .. spellName .. "|r to the queue.")
end
break
end
end
- elseif not br.isChecked("Mute Queue") and not notOnCD then
+ elseif not br.functions.misc:isChecked("Mute Queue") and not notOnCD then
br._G.print("Spell |cFFFF0000" .. spellName .. "|r not added, cooldown greater than gcd.")
end
end
@@ -274,8 +400,8 @@ function cl:common(...)
------------------
--[[Queue Casted]]
if sourceName ~= nil then
- if br.isInCombat("player") and br.GetUnitIsUnit(sourceName, "player") then
- local castTime = select(4, br._G.GetSpellInfo(spell)) or 0
+ if br.functions.misc:isInCombat("player") and br.functions.unit:GetUnitIsUnit(sourceName, "player") then
+ local castTime = select(4, br.api.wow.GetSpellInfo(spell)) or 0
if
(param == "SPELL_CAST_SUCCESS" and castTime == 0) or (param == "SPELL_CAST_START" and castTime > 0) or
spell == br.lastCast
@@ -287,7 +413,7 @@ function cl:common(...)
for i = 1, #br.player.queue do
if spell == br.player.queue[i].id then
br._G.tremove(br.player.queue, i)
- if not br.isChecked("Mute Queue") then
+ if not br.functions.misc:isChecked("Mute Queue") then
br._G.print(
"Cast Success! - Removed |cFFFF0000" .. spellName .. "|r from the queue."
)
@@ -311,15 +437,15 @@ function cl:common(...)
source = "player"
end
if source == "player" and destination ~= nil then
- if br.read.debuffTracker[destination] == nil then
- br.read.debuffTracker[destination] = {}
+ if br.readers.combatLog.debuffTracker[destination] == nil then
+ br.readers.combatLog.debuffTracker[destination] = {}
end
- if br.read.debuffTracker[destination][spell] == nil then
- br.read.debuffTracker[destination][spell] = {}
+ if br.readers.combatLog.debuffTracker[destination][spell] == nil then
+ br.readers.combatLog.debuffTracker[destination][spell] = {}
end
- br.read.debuffTracker[destination][spell][1] = source
- br.read.debuffTracker[destination][spell][2] = spell
- br.read.debuffTracker[destination][spell][3] = destination
+ br.readers.combatLog.debuffTracker[destination][spell][1] = source
+ br.readers.combatLog.debuffTracker[destination][spell][2] = spell
+ br.readers.combatLog.debuffTracker[destination][spell][3] = destination
end
end
end
@@ -331,9 +457,9 @@ function cl:common(...)
local thisUnit = "target"
if br.unlocked then --EWT then
local destination = br._G.GetObjectWithGUID(destination)
- if br.GetObjectExists(destination) then
+ if br.functions.unit:GetObjectExists(destination) then
thisUnit = destination
- elseif br.GetObjectExists("target") then
+ elseif br.functions.unit:GetObjectExists("target") then
thisUnit = br._G.GetObjectWithGUID(br._G.UnitGUID("target"))
else
thisUnit = br._G.GetObjectWithGUID(br._G.UnitGUID("player"))
@@ -345,7 +471,7 @@ function cl:common(...)
if param == "SPELL_AURA_REMOVED" then
if
not br._G.UnitAffectingCombat("player") or not br._G.UnitExists(thisUnit) or
- br.GetUnitIsDeadOrGhost(thisUnit)
+ br.functions.unit:GetUnitIsDeadOrGhost(thisUnit)
then
if pandemic[thisUnit] ~= nil then
pandemic[thisUnit] = nil
@@ -385,8 +511,8 @@ function cl:common(...)
---------------
--[[ Debug --]]
if
- br.getOptionCheck("Rotation Log") == true and source == br.guid and
- (param == "SPELL_CAST_SUCCESS" or (param == "SPELL_CAST_FAILED" and br.getOptionCheck("Display Failcasts")))
+ br.functions.misc:getOptionCheck("Rotation Log") == true and source == guid and
+ (param == "SPELL_CAST_SUCCESS" or (param == "SPELL_CAST_FAILED" and br.functions.misc:getOptionCheck("Display Failcasts")))
then
-- available locals
-- timeStamp, param, hideCaster, source, sourceName, sourceFlags, sourceRaidFlags, destination,
@@ -435,8 +561,8 @@ function cl:common(...)
br.castCount = br.lastCount
end
-- Blizz br._G.CastSpellByName bug bypass
- if br._G.GetSpellInfo(spell) == br._G.GetSpellInfo(br.botSpell) and spell ~= br.botSpell then
- -- Print("Spell Error Bypass: Correct ID = "..botSpell..", Incorrect ID = "..spell..", on "..botUnit)
+ local resolvedBotSpell = type(br.botSpell) == "table" and (br.functions.spell and br.functions.spell.getHighestKnownRank and br.functions.spell:getHighestKnownRank(br.botSpell) or br.botSpell[1]) or br.botSpell
+ if br.api.wow.GetSpellInfo(spell) == br.api.wow.GetSpellInfo(resolvedBotSpell) and spell ~= resolvedBotSpell then
br.castID = true
end
end
@@ -454,7 +580,7 @@ function cl:common(...)
end
local Power = " Power: " .. br._G.UnitPower("player")
-- create display row
- -- local textString = color..br.data.successCasts..red.."/"..white..br.getCombatTime()..red.."/"..color..spellName
+ -- local textString = color..br.data.successCasts..red.."/"..white..br.functions.combat:getCombatTime()..red.."/"..color..spellName
-- ..red..debugdest..color..debugSpell.."|cffFFDD11"..Power
-- string.format("%-25s", debugdest)
local textString =
@@ -463,7 +589,7 @@ function cl:common(...)
white ..
"| " ..
yellow ..
- string.format("%-3.3f", br.getCombatTime()) ..
+ string.format("%-3.3f", br.functions.combat:getCombatTime()) ..
white ..
"| " ..
color ..
@@ -481,7 +607,7 @@ function cl:common(...)
end
end
--[[ Last Spell Cast Success ]]
- if source == br.guid and param == "SPELL_CAST_SUCCESS" then
+ if source == guid and param == "SPELL_CAST_SUCCESS" then
-- Add spells we dont want to appear here.
if spell ~= 155521 then -- Auspicious Spirits
br.secondLastSpellCastSucess = br.lastSpellCastSuccess
@@ -490,7 +616,7 @@ function cl:common(...)
end
end
--[[ Last Spell Cast Started ]]
- if source == br.guid and (param == "SPELL_CAST_START" or param == "SPELL_CAST_SUCCESS") then
+ if source == guid and (param == "SPELL_CAST_START" or param == "SPELL_CAST_SUCCESS") then
-- Add spells we dont want to appear here.
if spell ~= 120361 or spell ~= 75 then -- Barrage fires
if param == "SPELL_CAST_SUCCESS" and (spell ~= 77767 or spell ~= 163485) or param == "SPELL_CAST_START" then
@@ -523,7 +649,7 @@ function cl:common(...)
end
end
-function cl:Deathknight(...)
+function combatLog:Deathknight(...)
local timeStamp,
param,
hideCaster,
@@ -550,7 +676,7 @@ function cl:Deathknight(...)
end
end
-function cl:DemonHunter(...)
+function combatLog:DemonHunter(...)
local timeStamp,
param,
hideCaster,
@@ -578,7 +704,7 @@ function cl:DemonHunter(...)
end
end
-function cl:Druid(...)
+function combatLog:Druid(...)
local timeStamp,
param,
hideCaster,
@@ -597,22 +723,22 @@ function cl:Druid(...)
-----------
-- Kitty ---------------
--[[ Bleed Recorder --]]
- if br._G.GetSpecialization() == 2 then
+ if br._G.C_SpecializationInfo.GetSpecialization() == 2 then
if source == br._G.UnitGUID("player") then
if destination ~= nil and destination ~= "" then
local thisUnit = "target"
if br.unlocked then --EWT then
local destination = br._G.GetObjectWithGUID(destination)
- if br.GetObjectExists(destination) then
+ if br.functions.unit:GetObjectExists(destination) then
thisUnit = destination
- elseif br.GetObjectExists("target") then
- thisUnit = br._G.GetObjectWithGUID(br._G.UnitGUID("target"))
+ elseif br.functions.unit:GetObjectExists("target") then
+ thisUnit = br._G.GetObjectWithGUID("target")
else
- thisUnit = br._G.GetObjectWithGUID(br._G.UnitGUID("player"))
+ thisUnit = br._G.GetObjectWithGUID("player")
end
- if br.player ~= nil and br.getDistance(thisUnit) < 40 then
+ if br.player ~= nil and br.functions.range:getDistance(thisUnit) < 40 then
local debuff = br.player.debuff
- local debuffID = br.player["spell"].debuffs
+ local debuffID = br.player["spells"].debuffs
if debuffID ~= nil then
local k
if spell == debuffID.rake or spell == debuffID.rip then
@@ -630,13 +756,13 @@ function cl:Druid(...)
end
if param == "SPELL_AURA_REMOVED" then
debuff[k].bleed[thisUnit] = 0
- if br.GetUnitIsUnit(thisUnit, "target") then
+ if br.functions.unit:GetUnitIsUnit(thisUnit, "target") then
debuff[k].bleed["target"] = 0
end
end
if param == "SPELL_AURA_APPLIED" or param == "SPELL_AURA_REFRESH" then
debuff[k].bleed[thisUnit] = debuff[k].calc()
- if br.GetUnitIsUnit(thisUnit, "target") then
+ if br.functions.unit:GetUnitIsUnit(thisUnit, "target") then
debuff[k].bleed["target"] = debuff[k].calc()
end
end
@@ -653,7 +779,7 @@ function cl:Druid(...)
br.shroomsTable = {}
br.shroomsTable[1] = {}
end
- if source == br.guid and param == "SPELL_SUMMON" and (spell == 147349 or spell == 145205) then
+ if source == guid and param == "SPELL_SUMMON" and (spell == 147349 or spell == 145205) then
br.shroomsTable[1].guid = destination
br.shroomsTable[1].x = nil
br.shroomsTable[1].y = nil
@@ -665,7 +791,7 @@ function cl:Druid(...)
then
br.shroomsTable[1] = {}
end
- if source == br.guid and br.class == 11 and br._G.GetSpecialization() == 1 then
+ if source == guid and class == 11 and br._G.C_SpecializationInfo.GetSpecialization() == 1 then
-- Starsurge Casted
if spell == 78674 and param == "SPELL_CAST_SUCCESS" then
if br.core then
@@ -675,7 +801,7 @@ function cl:Druid(...)
end
end
-function cl:Evoker(...)
+function combatLog:Evoker(...)
local timeStamp,
param,
hideCaster,
@@ -691,12 +817,12 @@ function cl:Evoker(...)
spellName,
_,
spellType = br._G.CombatLogGetCurrentEventInfo()
- -- if br.GetUnitIsUnit(sourceName, "player") then
+ -- if br.functions.unit:GetUnitIsUnit(sourceName, "player") then
-- print("Param: "..tostring(param)..", Spell: "..tostring(spell)..", SpellName: "..tostring(spellName))
-- end
end
-function cl:Hunter(...)
+function combatLog:Hunter(...)
local timeStamp,
param,
hideCaster,
@@ -714,19 +840,19 @@ function cl:Hunter(...)
spellType = br._G.CombatLogGetCurrentEventInfo()
--[[ Steady Focus ]]
if spell == 77767 and param == "SPELL_CAST_SUCCESS" then
- if br.data.settings[br.selectedSpec]["1stFocus"] ~= true then
- br.data.settings[br.selectedSpec]["1stFocus"] = true
+ if br.data.settings[br.loader.selectedSpec]["1stFocus"] ~= true then
+ br.data.settings[br.loader.selectedSpec]["1stFocus"] = true
else
- br.data.settings[br.selectedSpec]["1stFocus"] = false
+ br.data.settings[br.loader.selectedSpec]["1stFocus"] = false
end
end
--[[ Dead Pet Reset ]]
if br._G.UnitHealth("pet") > 0 then
- br.deadPet = false
+ br.readers.combatLog.deadPet = false
end
end
-function cl:Mage(...)
+function combatLog:Mage(...)
local timeStamp,
param,
hideCaster,
@@ -742,7 +868,7 @@ function cl:Mage(...)
spellName,
_,
spellType = br._G.CombatLogGetCurrentEventInfo()
- if source == br.guid then
+ if source == guid then
-- Params
-- SPELL
-- SPEL_PERIODIC
@@ -783,7 +909,7 @@ function cl:Mage(...)
end
end
-function cl:Monk(...)
+function combatLog:Monk(...)
local timeStamp,
param,
hideCaster,
@@ -800,47 +926,47 @@ function cl:Monk(...)
_,
spellType = br._G.CombatLogGetCurrentEventInfo()
-- if prevCombo == nil or not UnitAffectingCombat("player") then prevCombo = 6603 end
- if br.player ~= nil and br._G.GetSpecialization() == 3 and br.player.spells.fistsOfFury ~= nil then
- local myspell = br.player.spells
- local var = br.player.variables
- local comboSpells = {
- [myspell.bonedustBrew] = true,
- [myspell.blackoutKick] = true,
- [myspell.chiBurst] = true,
- [myspell.chiWave] = true,
- [myspell.cracklingJadeLightning] = true,
- [myspell.expelHarm] = true,
- [myspell.faelineStomp] = true,
- [myspell.fistsOfFury] = true,
- [myspell.flyingSerpentKick] = true,
- [myspell.risingSunKick] = true,
- [myspell.rushingJadeWind] = true,
- [myspell.spinningCraneKick] = true,
- [myspell.tigerPalm] = true,
- [myspell.touchOfDeath] = true,
- [myspell.weaponsOfOrder] = true,
- [myspell.whirlingDragonPunch] = true,
- }
- if var.lastCombo == nil or not br._G.UnitAffectingCombat("player") then
- var.lastCombo = 6603
- end
- if sourceName ~= nil then
- if br.isInCombat("player") and br.GetUnitIsUnit(sourceName, "player") then
- -- Last Combo
- if param == "SPELL_CAST_SUCCESS" then
- -- Print("Last Successful Spell was "..GetSpellInfo(spell).." with ID: "..spell)
- if comboSpells[spell] and spell ~= var.lastCombo then
- -- prevCombo = lastCombo
- var.lastCombo = spell
- -- Print(GetSpellInfo(var.lastCombo).." Success! ")--- Prev Last Combo was: "..GetSpellInfo(prevCombo))
- end
- end
- end
- end
- end
+ -- if br.player ~= nil and br._G.C_SpecializationInfo.GetSpecialization() == 3 and br.player.spells.fistsOfFury ~= nil then
+ -- local myspell = br.player.spells
+ -- local var = br.player.variables
+ -- local comboSpells = {
+ -- [myspell.bonedustBrew] = true,
+ -- [myspell.blackoutKick] = true,
+ -- [myspell.chiBurst] = true,
+ -- [myspell.chiWave] = true,
+ -- [myspell.cracklingJadeLightning] = true,
+ -- [myspell.expelHarm] = true,
+ -- [myspell.faelineStomp] = true,
+ -- [myspell.fistsOfFury] = true,
+ -- [myspell.flyingSerpentKick] = true,
+ -- [myspell.risingSunKick] = true,
+ -- [myspell.rushingJadeWind] = true,
+ -- [myspell.spinningCraneKick] = true,
+ -- [myspell.tigerPalm] = true,
+ -- [myspell.touchOfDeath] = true,
+ -- [myspell.weaponsOfOrder] = true,
+ -- [myspell.whirlingDragonPunch] = true,
+ -- }
+ -- if var.lastCombo == nil or not br._G.UnitAffectingCombat("player") then
+ -- var.lastCombo = 6603
+ -- end
+ -- if sourceName ~= nil then
+ -- if br.functions.misc:isInCombat("player") and br.functions.unit:GetUnitIsUnit(sourceName, "player") then
+ -- -- Last Combo
+ -- if param == "SPELL_CAST_SUCCESS" then
+ -- -- Print("Last Successful Spell was "..GetSpellInfo(spell).." with ID: "..spell)
+ -- if comboSpells[spell] and spell ~= var.lastCombo then
+ -- -- prevCombo = lastCombo
+ -- var.lastCombo = spell
+ -- -- Print(GetSpellInfo(var.lastCombo).." Success! ")--- Prev Last Combo was: "..GetSpellInfo(prevCombo))
+ -- end
+ -- end
+ -- end
+ -- end
+ -- end
end
-function cl:Priest(...)
+function combatLog:Priest(...)
local timeStamp,
param,
hideCaster,
@@ -856,26 +982,26 @@ function cl:Priest(...)
spellName,
_,
spellType = br._G.CombatLogGetCurrentEventInfo()
- if br._G.GetSpecialization() == 3 then
+ if br._G.C_SpecializationInfo.GetSpecialization() == 3 then
-- Periodic Damage Events
if param == "SPELL_PERIODIC_DAMAGE" then
if br.mfTicks == nil then
br.mfTicks = 0
end
-- Mindflay Ticks
- if source == br.guid and spellName == br._G.GetSpellInfo(15407) then
+ if source == guid and spellName == br.api.wow.GetSpellInfo(15407) then
br.mfTicks = br.mfTicks + 1
- br.addonDebug("Mindflay + 1 tick" .. "Total Ticks: " .. br.mfTicks)
+ br.functions.misc:addonDebug("Mindflay + 1 tick" .. "Total Ticks: " .. br.mfTicks)
end
end
-- Corruption was removed.
if param == "SPELL_AURA_REMOVED" then
- if source == br.guid then
+ if source == guid then
-- Mindflay
- if spellName == br._G.GetSpellInfo(15407) then
+ if spellName == br.api.wow.GetSpellInfo(15407) then
br.mfTicks = 0
br.maxmfTicks = 6
- br.addonDebug("Mindflay ticks reset")
+ br.functions.misc:addonDebug("Mindflay ticks reset")
end
end
end
@@ -886,19 +1012,19 @@ function cl:Priest(...)
br.msTicks = 0
end
-- Mind Sear Ticks
- if source == br.guid and spellName == br._G.GetSpellInfo(48045) and destination == br._G.UnitGUID("target") then
+ if source == guid and spellName == br.api.wow.GetSpellInfo(48045) and destination == br._G.UnitGUID("target") then
br.msTicks = br.msTicks + 1
- br.addonDebug("Mind Sear + 1 tick" .. "Total Ticks: " .. br.msTicks)
+ br.functions.misc:addonDebug("Mind Sear + 1 tick" .. "Total Ticks: " .. br.msTicks)
end
end
-- Corruption was removed.
if param == "SPELL_AURA_REMOVED" then
- if source == br.guid then
+ if source == guid then
-- Mindflay
- if spellName == br._G.GetSpellInfo(48045) then
+ if spellName == br.api.wow.GetSpellInfo(48045) then
br.msTicks = 0
br.maxmsTicks = 6
- br.addonDebug("Mind Sear ticks reset")
+ br.functions.misc:addonDebug("Mind Sear ticks reset")
end
end
end
@@ -943,7 +1069,7 @@ function cl:Priest(...)
end
end
-function cl:Paladin(...)
+function combatLog:Paladin(...)
local timeStamp,
param,
hideCaster,
@@ -961,19 +1087,19 @@ function cl:Paladin(...)
spellType = br._G.CombatLogGetCurrentEventInfo()
-----------------------
--[[ Class Trinket ]]
- if (source == br.guid and (spell == 35395 or spell == 53595)) then
+ if (source == guid and (spell == 35395 or spell == 53595)) then
br.previousT18classTrinket = destination
end
- if (source == br.guid and param == "SPELL_HEAL" and (spell == 184910 or spell == 185101)) then
+ if (source == guid and param == "SPELL_HEAL" and (spell == 184910 or spell == 185101)) then
br.protPaladinClassTrinketProc = br._G.GetTime()
end
--[[ Double Jeopardy ]]
- if spell == 20271 and source == br.guid and br.previousJudgmentTarget ~= destination then
+ if spell == 20271 and source == guid and br.previousJudgmentTarget ~= destination then
br.previousJudgmentTarget = destination
end
end
-function cl:Rogue(...)
+function combatLog:Rogue(...)
local timeStamp,
param,
hideCaster,
@@ -1002,20 +1128,20 @@ function cl:Rogue(...)
br.pickPocketing = false
end
--[[ Bleed Recorder --]]
- if br._G.GetSpecialization() == 1 then
+ if br._G.C_SpecializationInfo.GetSpecialization() == 1 then
if source == br._G.UnitGUID("player") then
if destination ~= nil and destination ~= "" then
local thisUnit = "target"
if br.unlocked then --EWT then
local destination = br._G.GetObjectWithGUID(destination)
- if br.GetObjectExists(destination) then
+ if br.functions.unit:GetObjectExists(destination) then
thisUnit = destination
- elseif br.GetObjectExists("target") then
+ elseif br.functions.unit:GetObjectExists("target") then
thisUnit = br._G.GetObjectWithGUID(br._G.UnitGUID("target"))
else
thisUnit = br._G.GetObjectWithGUID(br._G.UnitGUID("player"))
end
- if br.player ~= nil and br.getDistance(thisUnit) < 40 then
+ if br.player ~= nil and br.functions.range:getDistance(thisUnit) < 40 then
local debuff = br.player.debuff
local debuffID = br.player.spells.debuffs
local k
@@ -1028,7 +1154,7 @@ function cl:Rogue(...)
debuff.rupture.exsa[thisUnit] = true
debuff.garrote.exsa[thisUnit] = true
debuff.crimsonTempest.exsa[thisUnit] = true
- if br.GetUnitIsUnit(thisUnit, "target") then
+ if br.functions.unit:GetUnitIsUnit(thisUnit, "target") then
debuff.rupture.exsa["target"] = true
debuff.garrote.exsa["target"] = true
debuff.crimsonTempest.exsa["target"] = true
@@ -1043,7 +1169,7 @@ function cl:Rogue(...)
if param == "SPELL_AURA_APPLIED" or param == "SPELL_AURA_REFRESH" then
debuff[k].bleed[thisUnit] = debuff[k].calc()
debuff[k].exsa[thisUnit] = false
- if br.GetUnitIsUnit(thisUnit, "target") then
+ if br.functions.unit:GetUnitIsUnit(thisUnit, "target") then
debuff[k].bleed["target"] = debuff[k].calc()
debuff[k].exsa["target"] = false
end
@@ -1051,7 +1177,7 @@ function cl:Rogue(...)
if param == "SPELL_AURA_REMOVED" then
debuff[k].bleed[thisUnit] = 0
debuff[k].exsa[thisUnit] = false
- if br.GetUnitIsUnit(thisUnit, "target") then
+ if br.functions.unit:GetUnitIsUnit(thisUnit, "target") then
debuff[k].bleed["target"] = 0
debuff[k].exsa["target"] = false
end
@@ -1064,10 +1190,10 @@ function cl:Rogue(...)
end
end
-- OUTLAW
- if br._G.GetSpecialization() == 2 then
+ if br._G.C_SpecializationInfo.GetSpecialization() == 2 then
if source == br._G.UnitGUID("player") then
if spell == 287916 then
- br.vigorstacks = br.getBuffStacks("player", 287916) or 0
+ br.vigorstacks = br.functions.aura:getBuffStacks("player", 287916) or 0
br.vigorupdate = br._G.GetTime()
--print(br.vigorstacks..", "..br.vigorupdate)
end
@@ -1075,7 +1201,7 @@ function cl:Rogue(...)
end
end
-function cl:Shaman(...) -- 7
+function combatLog:Shaman(...) -- 7
local timeStamp,
param,
hideCaster,
@@ -1093,18 +1219,18 @@ function cl:Shaman(...) -- 7
spellType = br._G.CombatLogGetCurrentEventInfo()
--------------------
--[[ Fire Totem ]]
- if source == br.guid and param == "SPELL_SUMMON" and (spell == br.player.spells.seaingTotem or spell == br.player.spells.magmaTotem) then
+ if source == guid and param == "SPELL_SUMMON" and (spell == br.player.spells.searingTotem or spell == br.player.spells.magmaTotem) then
br.activeTotem = destination
- br.activeTotemPosition = br.GetObjectPosition("player")
+ br.activeTotemPosition = br.functions.unit:GetObjectPosition("player")
end
if param == "UNIT_DESTROYED" and br.activeTotem == destination then
br.activeTotem = nil
end
-------------
--[[ Lightning Bolt ]]
- if br.player ~= nil and br._G.GetSpecialization() == 2 then
+ if br.player ~= nil and br._G.C_SpecializationInfo.GetSpecialization() == 2 then
if sourceName ~= nil then
- if br.isInCombat("player") and br.GetUnitIsUnit(sourceName, "player") then
+ if br.functions.misc:isInCombat("player") and br.functions.unit:GetUnitIsUnit(sourceName, "player") then
-- Chain Lightning / Lightning Bolt
if br.lightningStarted == nil then
br.lightningStarted = false
@@ -1132,7 +1258,7 @@ function cl:Shaman(...) -- 7
end
end
-function cl:Warlock(...) -- 9
+function combatLog:Warlock(...) -- 9
local timeStamp,
param,
hideCaster,
@@ -1148,21 +1274,21 @@ function cl:Warlock(...) -- 9
spellName,
_,
spellType = br._G.CombatLogGetCurrentEventInfo()
- if br._G.GetSpecialization() == 1 then
- if source == br.guid and param == "UNIT_SPELLCAST_CHANNEL_START" then
+ if br._G.C_SpecializationInfo.GetSpecialization() == 1 then
+ if source == guid and param == "UNIT_SPELLCAST_CHANNEL_START" then
-- Drain Soul counter
- if br._G.UnitChannelInfo("player") == br._G.GetSpellInfo(198590) then
+ if br._G.UnitChannelInfo("player") == br.api.wow.GetSpellInfo(198590) then
br.dsTicks = 1
end
end
-- We stopped a channel, reset counters.
- if source == br.guid and param == "UNIT_SPELLCAST_CHANNEL_STOP" then
+ if source == guid and param == "UNIT_SPELLCAST_CHANNEL_STOP" then
br.dsTicks = 1
end
-- CLear dot table after each death/individual combat scenarios.
- if source == br.guid and param == "PLAYER_REGEN_ENABLED" or param == "PLAYER_REGEN_DISABLED" then
+ if source == guid and param == "PLAYER_REGEN_ENABLED" or param == "PLAYER_REGEN_DISABLED" then
end
if param == "UNIT_DIED" then
@@ -1172,7 +1298,7 @@ function cl:Warlock(...) -- 9
-- Corruption was refreshed.
if param == "SPELL_AURA_REFRESH" then
-- Drain Soul
- if source == br.guid and spellName == br._G.GetSpellInfo(198590) then
+ if source == guid and spellName == br.api.wow.GetSpellInfo(198590) then
br.dsTicks = 1
br.maxdsTicks = 5
end
@@ -1180,7 +1306,7 @@ function cl:Warlock(...) -- 9
-- Successfull Spell Casts
if param == "SPELL_CAST_SUCCESS" then
- if source == br.guid then
+ if source == guid then
end
end
@@ -1190,56 +1316,56 @@ function cl:Warlock(...) -- 9
br.dsTicks = 0
end
-- Drain Soul Ticks
- if source == br.guid and spellName == br._G.GetSpellInfo(198590) then
+ if source == guid and spellName == br.api.wow.GetSpellInfo(198590) then
br.dsTicks = br.dsTicks + 1
- br.addonDebug("Drain Soul + 1 tick" .. "Total Ticks: " .. br.dsTicks)
+ br.functions.misc:addonDebug("Drain Soul + 1 tick" .. "Total Ticks: " .. br.dsTicks)
end
end
-- Corruption was removed.
if param == "SPELL_AURA_REMOVED" then
- if source == br.guid then
+ if source == guid then
-- Drain Soul
- if spellName == br._G.GetSpellInfo(198590) then
+ if spellName == br.api.wow.GetSpellInfo(198590) then
br.dsTicks = 1
br.maxdsTicks = 5
- br.addonDebug("Drain Soul ticks reset")
+ br.functions.misc:addonDebug("Drain Soul ticks reset")
end
end
end
-- Corruption was applied.
if param == "SPELL_AURA_APPLIED" then
- if source == br.guid then
+ if source == guid then
end
end
end
- if br._G.GetSpecialization() == 2 then
- -- if source == br.guid and param == "SPELL_CAST_SUCCESS" then
+ if br._G.C_SpecializationInfo.GetSpecialization() == 2 then
+ -- if source == guid and param == "SPELL_CAST_SUCCESS" then
-- -- Hand of Guldan
-- if == 105174 then
- -- if not br.lastCastTable.hog then br.lastCastTable.hog = {} end
+ -- if not br.functions.lastCast.lastCastTable.hog then br.functions.lastCast.lastCastTable.hog = {} end
-- if br.lastCast then
- -- tinsert(br.lastCastTable.hog, 1, GetTime())
- -- if #br.lastCastTable.hog == 5 then
- -- br.lastCastTable.hog[5] = nil
+ -- tinsert(br.functions.lastCast.lastCastTable.hog, 1, GetTime())
+ -- if #br.functions.lastCast.lastCastTable.hog == 5 then
+ -- br.functions.lastCast.lastCastTable.hog[5] = nil
-- end
-- end
-- end
-- -- Line CD
- -- if not br.lastCastTable.line_cd then br.lastCastTable.line_cd = {} end
- -- br.lastCastTable.line_cd[] = GetTime()
+ -- if not br.functions.lastCast.lastCastTable.line_cd then br.functions.lastCast.lastCastTable.line_cd = {} end
+ -- br.functions.lastCast.lastCastTable.line_cd[] = GetTime()
-- end
-- -- Demonology Manager
-- -- Imps are summoned
- -- if param == "SPELL_SUMMON" and source == br.guid and ( == 104317 or == 279910) then
+ -- if param == "SPELL_SUMMON" and source == guid and ( == 104317 or == 279910) then
-- print("Imp SUMMON")
-- end
-- -- Other Demons are summoned
- -- if param == "SPELL_SUMMON" and source == br.guid and not (spell == 104317 or spell == 279910) then
+ -- if param == "SPELL_SUMMON" and source == guid and not (spell == 104317 or spell == 279910) then
-- print("Demon SUMMON")
-- end
end
- if br._G.GetSpecialization() == 3 then
+ if br._G.C_SpecializationInfo.GetSpecialization() == 3 then
-- last Immolate
if param == "SPELL_CAST_SUCCESS" and spell == 348 then
br.lastImmolateTarget = destination
@@ -1248,7 +1374,7 @@ function cl:Warlock(...) -- 9
end
---------------------
--[[ Pet Manager --]]
- if source == br.guid and param == "SPELL_CAST_SUCCESS" then
+ if source == guid and param == "SPELL_CAST_SUCCESS" then
if spell == 688 or spell == 112866 then
br.petSummoned = 1
br.petSummonedTime = br._G.GetTime()
@@ -1272,7 +1398,7 @@ function cl:Warlock(...) -- 9
end
end
-function cl:Warrior(...)
+function combatLog:Warrior(...)
local timeStamp,
param,
hideCaster,
@@ -1292,18 +1418,18 @@ function cl:Warrior(...)
--[[ Bleed Recorder (Warrior) --]]
if destName == br._G.UnitName("player") then
br.reflectPlayer = true
- elseif br.timer:useTimer("reflect reset", 0.8) then
+ elseif br.debug.timer:useTimer("reflect reset", 0.8) then
br.reflectPlayer = false
end
- if br._G.GetSpecialization("player") == 1 then
+ if br._G.C_SpecializationInfo.GetSpecialization("player") == 1 then
-- snapshot on spellcast
- if source == br.guid and param == "SPELL_CAST_SUCCESS" then
+ if source == guid and param == "SPELL_CAST_SUCCESS" then
-- but only record the snapshot if it successfully applied
if spell == 115767 then
br.deepWoundsCastAP = br._G.UnitAttackPower("player")
end
elseif
- source == br.guid and (param == "SPELL_AURA_APPLIED" or param == "SPELL_AURA_REFRESH") and
+ source == guid and (param == "SPELL_AURA_APPLIED" or param == "SPELL_AURA_REFRESH") and
br.deepWoundsCastAP ~= nil
then
if spell == 115767 then
diff --git a/System/readers/common.lua b/System/readers/common.lua
index 61d820059f..fc69bc7d18 100644
--- a/System/readers/common.lua
+++ b/System/readers/common.lua
@@ -1,6 +1,8 @@
local _, br = ...
+br.readers.common = br.readers.common or {}
+local common = br.readers.common
-function br.read.commonReaders()
+function common:commonReaders()
---------------
--[[ Readers ]]
---------------
@@ -9,8 +11,8 @@ function br.read.commonReaders()
local Frame = br._G.CreateFrame("Frame")
Frame:RegisterEvent("BAG_UPDATE")
local function BagUpdate(_, event, _)
- if event == "BAG_UPDATE" then
- br.bagsUpdated = true
+ if event == "BAG_UPDATE" and br.player then
+ br.player.bagsUpdated = true
end
end
Frame:SetScript("OnEvent", BagUpdate)
@@ -27,9 +29,9 @@ function br.read.commonReaders()
Frame = br._G.CreateFrame("Frame")
Frame:RegisterEvent("LFG_PROPOSAL_SHOW")
local function MerchantShow_AutoJoin(_, event, _)
- if br.getOptionCheck("Accept Queues") == true then
+ if br.functions.misc:getOptionCheck("Accept Queues") == true then
if event == "LFG_PROPOSAL_SHOW" then
- br.readyToAccept = br._G.GetTime()
+ br.functions.misc.readyToAccept = br._G.GetTime()
end
end
end
@@ -50,20 +52,21 @@ function br.read.commonReaders()
-- Frame:SetScript("OnEvent", Eclipse)
--------------------------
--[[ isStanding Frame --]]
- br.DontMoveStartTime = nil
+ common.DontMoveStartTime = 0
+ common.isMovingStartTime = 0
br._G.CreateFrame("Frame"):SetScript(
"OnUpdate",
function()
if br._G.GetUnitSpeed("Player") == 0 then
- if not br.DontMoveStartTime then
- br.DontMoveStartTime = br._G.GetTime()
+ if not common.DontMoveStartTime then
+ common.DontMoveStartTime = br._G.GetTime()
end
- br.isMovingStartTime = 0
+ common.isMovingStartTime = 0
else
- if br.isMovingStartTime == 0 then
- br.isMovingStartTime = br._G.GetTime()
+ if common.isMovingStartTime == 0 then
+ common.isMovingStartTime = br._G.GetTime()
end
- br.DontMoveStartTime = nil
+ common.DontMoveStartTime = 0
end
end
)
@@ -88,8 +91,8 @@ function br.read.commonReaders()
Frame:RegisterEvent("MERCHANT_SHOW")
local function MerchantShow_AutoSellRepair(_, event, _)
if event == "MERCHANT_SHOW" then
- if br.getOptionCheck("Auto-Sell/Repair") then
- br.SellGreys()
+ if br.functions.misc:getOptionCheck("Auto-Sell/Repair") then
+ br.engines.lootEngine:sellGreys()
end
end
end
@@ -101,12 +104,12 @@ function br.read.commonReaders()
local function EnteringCombat(_, event, _)
if event == "PLAYER_REGEN_DISABLED" then
-- here we should manage stats snapshots
- br.AgiSnap = br.getAgility()
+ br.AgiSnap = br.functions.misc:getAgility()
if br.data.settings ~= nil then
- br.data.settings[br.selectedSpec]["Combat Started"] = br._G.GetTime()
+ br.data.settings[br.loader.selectedSpec]["Combat Started"] = br._G.GetTime()
end
- br.ChatOverlay("|cffFF0000Entering Combat")
+ br.ui.chatOverlay:Show("|cffFF0000Entering Combat")
end
end
Frame:SetScript("OnEvent", EnteringCombat)
@@ -130,16 +133,16 @@ function br.read.commonReaders()
br.data.successCasts = 0
br.data.failCasts = 0
if br.data.settings ~= nil then
- br.data.settings[br.selectedSpec]["Combat Started"] = 0
+ br.data.settings[br.loader.selectedSpec]["Combat Started"] = 0
end
- br.ChatOverlay("|cff00FF00Leaving Combat")
+ br.ui.chatOverlay:Show("|cff00FF00Leaving Combat")
-- clean up out of combat
br.Rip_sDamage = {}
br.Rake_sDamage = {}
br.Thrash_sDamage = {}
br.petAttacking = false
- br.lastCastTable.line_cd = {}
- br._G.wipe(br.read.debuffTracker)
+ br.functions.lastCast.lastCastTable.line_cd = {}
+ br._G.wipe(br.readers.combatLog.debuffTracker)
end
end
Frame:SetScript("OnEvent", LeavingCombat)
@@ -153,28 +156,28 @@ function br.read.commonReaders()
local param = br.lastError
--br._G.print("|cffFF0000UI Error: " .. errorType .. " - " .. param)
if param == "ERR_PET_SPELL_DEAD" then
- br.data.settings[br.selectedSpec]["Pet Dead"] = true
- br.data.settings[br.selectedSpec]["Pet Whistle"] = false
+ br.data.settings[br.loader.selectedSpec]["Pet Dead"] = true
+ br.data.settings[br.loader.selectedSpec]["Pet Whistle"] = false
end
if param == "PETTAME_NOTDEAD" .. "." then
- br.data.settings[br.selectedSpec]["Pet Dead"] = false
- br.data.settings[br.selectedSpec]["Pet Whistle"] = true
+ br.data.settings[br.loader.selectedSpec]["Pet Dead"] = false
+ br.data.settings[br.loader.selectedSpec]["Pet Whistle"] = true
end
if param == "SPELL_FAILED_ALREADY_HAVE_PET" then
- br.data.settings[br.selectedSpec]["Pet Dead"] = true
- br.data.settings[br.selectedSpec]["Pet Whistle"] = false
+ br.data.settings[br.loader.selectedSpec]["Pet Dead"] = true
+ br.data.settings[br.loader.selectedSpec]["Pet Whistle"] = false
end
if param == "PETTAME_CANTCONTROLEXOTIC" .. "." then
- if br.data.settings[br.selectedSpec]["Box PetManager"] < 5 then
- br.data.settings[br.selectedSpec]["Box PetManager"] = br.data.settings[br.selectedSpec]
+ if br.data.settings[br.loader.selectedSpec]["Box PetManager"] < 5 then
+ br.data.settings[br.loader.selectedSpec]["Box PetManager"] = br.data.settings[br.loader.selectedSpec]
["Box PetManager"] + 1
else
- br.data.settings[br.selectedSpec]["Box PetManager"] = 1
+ br.data.settings[br.loader.selectedSpec]["Box PetManager"] = 1
end
end
if param == "PETTAME_NOPETAVAILABLE" .. "." then
- br.data.settings[br.selectedSpec]["Pet Dead"] = false
- br.data.settings[br.selectedSpec]["Pet Whistle"] = true
+ br.data.settings[br.loader.selectedSpec]["Pet Dead"] = false
+ br.data.settings[br.loader.selectedSpec]["Pet Whistle"] = true
end
if param == "SPELL_FAILED_TARGET_NO_WEAPONS" then
br.isDisarmed = true
@@ -196,16 +199,16 @@ function br.read.commonReaders()
end
end
Frame:SetScript("OnEvent", UiErrorMessages)
- ------------------------
- --[[ Spells Changed --]]
- Frame = br._G.CreateFrame("Frame")
- Frame:RegisterEvent("LEARNED_SPELL_IN_SKILL_LINE")
- local function SpellsChanged(_, _, _)
- if not br.configReloadTimer or br.configReloadTimer <= br._G.GetTime() - 1 then
- br.currentConfig, br.configReloadTimer = nil, br._G.GetTime()
- end
- end
- Frame:SetScript("OnEvent", SpellsChanged)
+ -- ------------------------
+ -- --[[ Spells Changed --]]
+ -- Frame = br._G.CreateFrame("Frame")
+ -- Frame:RegisterEvent("LEARNED_SPELL_IN_SKILL_LINE")
+ -- local function SpellsChanged(_, _, _)
+ -- if not br.configReloadTimer or br.configReloadTimer <= br._G.GetTime() - 1 then
+ -- br.currentConfig, br.configReloadTimer = nil, br._G.GetTime()
+ -- end
+ -- end
+ -- Frame:SetScript("OnEvent", SpellsChanged)
--- under devlopment not working as of now
--[[ Addon reader ]]
-- local Frame = CreateFrame('Frame')
@@ -261,50 +264,7 @@ function br.read.commonReaders()
-- br._G.print(...)
-- end
--Frame:SetScript("OnEvent", addonReader)
- TooltipDataProcessor.AddTooltipPostCall(Enum.TooltipDataType.Unit,
- function(self)
- if br.unlocked --[[EWT]] and br._G.GetObjectCount() ~= nil then
- local _, lunit = self:GetUnit()
- if not br._G.UnitIsVisible(lunit) then
- return
- end
- local unit = br._G.UnitGUID(lunit)
- local burnUnit = br.getOptionCheck("Forced Burn") and br.isBurnTarget(unit) > 0
- local playerTarget = br.GetUnitIsUnit(unit, "target")
- local targeting = br.isTargeting(unit)
- local hasThreat = br.hasThreat(unit) or targeting or br.isInProvingGround() or burnUnit
- local reaction = br.GetUnitReaction(unit, "player") or 10
- if br.isChecked("Target Validation Debug") and (not br._G.UnitIsPlayer(unit) or br._G.UnitIsCharmed(unit) or br.UnitDebuffID("player", 320102)) then
- if br.isValidUnit(unit) then
- self:AddLine("Unit is Valid", 0, 1, 0)
- elseif not br.getLineOfSight("player", unit) then
- self:AddLine("LoS Fail", 1, 0, 0)
- elseif not (br.units[unit] ~= nil or br.GetUnitIsUnit(unit, "target") or br.lists.threatBypass[br.GetObjectID(unit)] ~= nil or burnUnit) then
- self:AddLine("Not in Units Table", 1, 0, 0)
- elseif not (not br._G.UnitIsTapDenied(unit) or br.isDummy) then
- self:AddLine("Unit is Tap Denied", 1, 0, 0)
- elseif not (br.isSafeToAttack(unit) or burnUnit) then
- self:AddLine("Safe Attack Fail", 1, 0, 0)
- elseif not ((reaction < 5 and not br.isChecked("Hostiles Only")) or (br.isChecked("Hostiles Only") and (reaction < 4 or playerTarget or targeting)) or br.isDummy or burnUnit) then
- self:AddLine("Reaction Value Fail", 1, 0, 0)
- elseif
- not ((br.isChecked("Attack MC Targets") and (not br.GetUnitIsFriend(unit, "player") or (br._G.UnitIsCharmed(unit) and br._G.UnitCanAttack("player", unit)))) or
- not br.GetUnitIsFriend(unit, "player"))
- then
- self:AddLine("MC Check Fail", 1, 0, 0)
- elseif br.getOptionCheck("Don't break CCs") and br.isLongTimeCCed(unit) then
- self:AddLine("CC Check Fail", 1, 0, 0)
- elseif not hasThreat then
- self:AddLine("Threat Fail", 1, 0, 0)
- elseif not br.enemyListCheck(unit) then
- self:AddLine("List Check Fail", 1, 0, 0)
- else
- self:AddLine("Validation Failed", 0, 0, 1)
- end
- end
- end
- end
- )
+
---------------------------
--[[ Combat Log Reader --]]
local superReaderFrame = br._G.CreateFrame("Frame")
@@ -321,14 +281,14 @@ function br.read.commonReaders()
superReaderFrame:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
superReaderFrame:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
superReaderFrame:RegisterEvent("UNIT_SPELLCAST_CHANNEL_UPDATE")
- superReaderFrame:RegisterEvent("UNIT_SPELLCAST_EMPOWER_START")
- superReaderFrame:RegisterEvent("UNIT_SPELLCAST_EMPOWER_STOP")
- superReaderFrame:RegisterEvent("UNIT_SPELLCAST_EMPOWER_UPDATE")
+ -- superReaderFrame:RegisterEvent("UNIT_SPELLCAST_EMPOWER_START")
+ -- superReaderFrame:RegisterEvent("UNIT_SPELLCAST_EMPOWER_STOP")
+ -- superReaderFrame:RegisterEvent("UNIT_SPELLCAST_EMPOWER_UPDATE")
superReaderFrame:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")
superReaderFrame:RegisterEvent("UNIT_POWER_UPDATE")
superReaderFrame:RegisterEvent("ENCOUNTER_START")
superReaderFrame:RegisterEvent("ENCOUNTER_END")
- superReaderFrame:RegisterUnitEvent("AZERITE_EMPOWERED_ITEM_SELECTION_UPDATED")
+ -- superReaderFrame:RegisterUnitEvent("AZERITE_EMPOWERED_ITEM_SELECTION_UPDATED")
superReaderFrame:RegisterUnitEvent("AZERITE_ESSENCE_ACTIVATED")
superReaderFrame:RegisterUnitEvent("PLAYER_EQUIPMENT_CHANGED")
superReaderFrame:RegisterUnitEvent("PLAYER_LEVEL_UP")
@@ -343,18 +303,18 @@ function br.read.commonReaders()
local unitTarget, updateInfo = ...
if updateInfo and updateInfo.addedAuras then
for _, aura in ipairs(updateInfo.addedAuras) do
- br.auraMaxStacks = br.auraMaxStacks or {}
- br.auraMaxStacks[aura.spellId] = br.auraMaxStacks[aura.spellId] or {}
- br.auraMaxStacks[aura.spellId][unitTarget] = aura.maxCharges or 0
+ br.readers.common.auraMaxStacks = br.readers.common.auraMaxStacks or {}
+ br.readers.common.auraMaxStacks[aura.spellId] = br.readers.common.auraMaxStacks[aura.spellId] or {}
+ br.readers.common.auraMaxStacks[aura.spellId][unitTarget] = aura.maxCharges or 0
end
end
end
-- Azerite Essence
- if event == "AZERITE_ESSENCE_ACTIVATED" then
- br.updatePlayerInfo = true
+ if event == "AZERITE_ESSENCE_ACTIVATED" and br.player then
+ br.player.updatePlayer = true
end
- if event == "PLAYER_EQUIPMENT_CHANGED" then
- br.equipHasChanged = true
+ if event == "PLAYER_EQUIPMENT_CHANGED" and br.player then
+ br.player.equipHasChanged = true
end
-- Player moving
if event == "PLAYER_STARTED_MOVING" then
@@ -370,11 +330,13 @@ function br.read.commonReaders()
return
end
-- Update Player Info
- -- if event == "PLAYER_TALENT_UPDATE" and select(2, GetSpecializationInfo(GetSpecialization())) == br.selectedSpec then
+ -- if event == "PLAYER_TALENT_UPDATE" and select(2, GetSpecializationInfo(GetSpecialization())) == br.loader.selectedSpec then
-- br.rotationChanged = true
-- end
- if event == "PLAYER_TALENT_UPDATE" or event == "PLAYER_LEVEL_UP" or event == "PLAYER_EQUIPMENT_CHANGED" or event == "AZERITE_EMPOWERED_ITEM_SELECTION_UPDATED" or event == "TRAIT_CONFIG_UPDATED" then
- br.updatePlayerInfo = true
+ if event == "PLAYER_TALENT_UPDATE" or event == "PLAYER_LEVEL_UP" or event == "PLAYER_EQUIPMENT_CHANGED" --[[or event == "AZERITE_EMPOWERED_ITEM_SELECTION_UPDATED"]] or event == "TRAIT_CONFIG_UPDATED" then
+ if br.player then
+ br.player.updatePlayer = true
+ end
end
-------------------------------------------------
--[[ SpellCast Sents (used to define target) --]]
@@ -456,14 +418,20 @@ function br.read.commonReaders()
if br.player ~= nil then
if #br.player.queue ~= 0 then
for i = 1, #br.player.queue do
- if br._G.GetSpellInfo(SpellID) == br._G.GetSpellInfo(br.player.queue[i].id) then
+ local queuedID = br.player.queue[i].id
+ if type(queuedID) == "table" and br.functions and br.functions.spell and br.functions.spell.getHighestKnownRank then
+ queuedID = br.functions.spell:getHighestKnownRank(queuedID) or queuedID[1]
+ elseif type(queuedID) == "table" then
+ queuedID = queuedID[1]
+ end
+ if br.api.wow.GetSpellInfo(SpellID) == br.api.wow.GetSpellInfo(queuedID) then
br._G.tremove(br.player.queue, i)
if br._G.IsAoEPending() then
br._G.SpellStopTargeting()
end
- if not br.isChecked("Mute Queue") then
+ if not br.functions.misc:isChecked("Mute Queue") then
br._G.print("Cast Success! - Removed |cFFFF0000" ..
- br._G.GetSpellInfo(SpellID) .. "|r from the queue.")
+ br.api.wow.GetSpellInfo(queuedID) .. "|r from the queue.")
end
break
end
@@ -503,7 +471,7 @@ function br.read.commonReaders()
local SourceUnit = select(1, ...)
local SpellID = select(3, ...)
local MyClass = br._G.UnitClass("player")
- if SourceUnit == "player" and br.isKnown(SpellID) then
+ if SourceUnit == "player" and br.functions.spell:isKnown(SpellID) then
-- Kill Command
if SpellID == 34026 then
---Print("Kill Command FAILED")
@@ -541,29 +509,29 @@ function br.read.commonReaders()
--Print("Channel Update")
end
end
- if event == "UNIT_SPELLCAST_EMPOWER_START" then
- local SourceUnit = select(1, ...)
- local SpellID = select(3, ...)
- if SourceUnit == "player" then
- --Print("Channel Start")
- br.empowerID = SpellID
- end
- end
- if event == "UNIT_SPELLCAST_EMPOWER_STOP" then
- local SourceUnit = select(1, ...)
- local SpellID = select(3, ...)
- if SourceUnit == "player" then
- --Print("Channel STOP")
- br.empowerID = 0
- end
- end
- if event == "UNIT_SPELLCAST_EMPOWER_UPDATE" then
- local SourceUnit = select(1, ...)
- -- local SpellID = select(3, ...)
- if SourceUnit == "player" then
- --Print("Channel Update")
- end
- end
+ -- if event == "UNIT_SPELLCAST_EMPOWER_START" then
+ -- local SourceUnit = select(1, ...)
+ -- local SpellID = select(3, ...)
+ -- if SourceUnit == "player" then
+ -- --Print("Channel Start")
+ -- br.empowerID = SpellID
+ -- end
+ -- end
+ -- if event == "UNIT_SPELLCAST_EMPOWER_STOP" then
+ -- local SourceUnit = select(1, ...)
+ -- local SpellID = select(3, ...)
+ -- if SourceUnit == "player" then
+ -- --Print("Channel STOP")
+ -- br.empowerID = 0
+ -- end
+ -- end
+ -- if event == "UNIT_SPELLCAST_EMPOWER_UPDATE" then
+ -- local SourceUnit = select(1, ...)
+ -- -- local SpellID = select(3, ...)
+ -- if SourceUnit == "player" then
+ -- --Print("Channel Update")
+ -- end
+ -- end
if event == "UI_ERROR_MESSAGE" then
local errorMsg = select(1, ...)
local messageErr = select(2, ...)
@@ -572,16 +540,16 @@ function br.read.commonReaders()
-- 203 = "Cannot attack while dead"
-- 278 = "Your Pet is not Dead" / "Your pet is dead. Use Revive Pet"
if errorMsg == 278 then
- local revive = br._G.GetSpellInfo(50769) -- Used for string matching error messasge.
+ local revive = br.api.wow.GetSpellInfo(50769) -- Used for string matching error messasge.
local match = string.find(messageErr, revive) ~= nil
if match then
- br.deadPet = true
+ br.readers.combatLog.deadPet = true
else
- br.deadPet = false
+ br.readers.combatLog.deadPet = false
end
end
- if not br.GetUnitIsDeadOrGhost("player") and (br.GetUnitIsDeadOrGhost("pet") or not br.GetUnitExists("pet")) and (errorMsg == 51 or errorMsg == 203) then --or errorMsg == 277 or errorMsg == 275 then
- br.deadPet = true
+ if not br.functions.unit:GetUnitIsDeadOrGhost("player") and (br.functions.unit:GetUnitIsDeadOrGhost("pet") or not br.functions.unit:GetUnitExists("pet")) and (errorMsg == 51 or errorMsg == 203) then --or errorMsg == 277 or errorMsg == 275 then
+ br.readers.combatLog.deadPet = true
-- if deadPet == false then
-- elseif deadPet == true and br._G.UnitHealth("pet") > 0 then
-- deadPet = false
diff --git a/System/ui/ui.lua b/System/ui/ui.lua
index 2d17724913..37ee60c121 100644
--- a/System/ui/ui.lua
+++ b/System/ui/ui.lua
@@ -1,12 +1,8 @@
-local LSM = LibStub("LibSharedMedia-3.0")
-local MediaType_FONT = LSM.MediaType.FONT
local DiesalGUI = _G.LibStub("DiesalGUI-1.0")
local _, br = ...
-- Global setup
-br.ui = {}
-br.spacing = 15
-LSM:Register(MediaType_FONT, "calibrib", [[Interface\Addons\SharedMedia_MyMedia\font\calibrib.ttf]])
---
+br.ui = br.ui or {}
+
local Type = "UI"
local BRFont = "DiesalFontNormal"
do
diff --git a/System/wowapi.lua b/System/wowapi.lua
new file mode 100644
index 0000000000..e2b1ec9b2f
--- /dev/null
+++ b/System/wowapi.lua
@@ -0,0 +1,47 @@
+-- WoW API Compatibility Layer
+-- Loads the appropriate API wrapper based on WoW version
+local _, br = ...
+
+-- Initialize API compatibility table
+br.api = br.api or {}
+br.api.compat = {}
+
+-- Detect WoW version
+br.isRetail = (WOW_PROJECT_ID == WOW_PROJECT_MAINLINE)
+---@diagnostic disable-next-line: undefined-global
+br.isMOP = (WOW_PROJECT_ID == WOW_PROJECT_MISTS_CLASSIC)
+br.isCata = (WOW_PROJECT_ID == WOW_PROJECT_CATACLYSM_CLASSIC)
+br.isWrath = (WOW_PROJECT_ID == WOW_PROJECT_WRATH_CLASSIC)
+br.isBC = (WOW_PROJECT_ID == WOW_PROJECT_BURNING_CRUSADE_CLASSIC)
+br.isClassic = (WOW_PROJECT_ID == WOW_PROJECT_CLASSIC)
+
+-- Set Expansion Name
+br.api.expansionName = br.isRetail and "Retail" or
+ br.isMOP and "MoP Classic" or
+ br.isCata and "Cataclysm Classic" or
+ br.isWrath and "Wrath Classic" or
+ br.isBC and "Burning Crusade Classic" or
+ br.isClassic and "Classic" or
+ "Unknown"
+
+-- Version info for debugging
+function br.api.compat:getVersionInfo()
+ local version, build, date, tocVersion = GetBuildInfo()
+ return {
+ version = version,
+ build = build,
+ date = date,
+ tocVersion = tocVersion,
+ isRetail = br.isRetail,
+ isMOP = br.isMOP,
+ isCata = br.isCata,
+ isWrath = br.isWrath,
+ isBC = br.isBC,
+ isClassic = br.isClassic,
+ projectID = WOW_PROJECT_ID,
+ expansionName = br.api.expansionName
+ }
+end
+
+-- This will be populated by the expansion-specific file
+br.api.wow = {}
diff --git a/Unlockers/gg.lua b/Unlockers/gg.lua
deleted file mode 100644
index 4daa7f8c91..0000000000
--- a/Unlockers/gg.lua
+++ /dev/null
@@ -1,478 +0,0 @@
---------------------------------------------------------------------------------------------------------------------------------
--- unlockList
---------------------------------------------------------------------------------------------------------------------------------
-local unlockList =
-{
- "AcceptBattlefieldPort",
- "AcceptProposal",
- "AcceptTrade",
- "AscendStop",
- "AssistUnit",
- "AttackTarget",
- "CameraOrSelectOrMoveStart",
- "CameraOrSelectOrMoveStop",
- "CancelItemTempEnchantment",
- "CancelLogout",
- "CancelShapeshiftForm",
- "CancelUnitBuff",
- "CanSummonFriend",
- "CastPetAction",
- "CastShapeshiftForm",
- "CastSpell",
- "CastSpellByID",
- "CastSpellByName",
- "ChangeActionBarPage",
- "CheckInteractDistance",
- "ClearOverrideBindings",
- "ClearTarget",
- "CombatTextSetActiveUnit",
- "CopyToClipboard",
- "CreateMacro",
- "DeleteCursorItem",
- "DeleteMacro",
- "DemoteAssistant",
- "DescendStop",
- "DestroyTotem",
- "DropItemOnUnit",
- "FocusUnit",
- "FollowUnit",
- "ForceQuit",
- "GetCursorPosition",
- "GetDefaultLanguage",
- "GetPartyAssignment",
- "GetPlayerInfoByGUID",
- "GetRaidTargetIndex",
- "GetReadyCheckStatus",
- "GetUnitName",
- "GetUnitSpeed",
- "GetUnscaledFrameRect",
- "GuildControlSetRank",
- "GuildControlSetRankFlag",
- "GuildDemote",
- "GuildPromote",
- "GuildUninvite",
- "InitiateTrade",
- "InteractUnit",
- "IsItemInRange",
- "IsSpellInRange",
- "JoinBattlefield",
- "JumpOrAscendStart",
- "Logout",
- "MoveBackwardStart",
- "MoveBackwardStop",
- "MoveForwardStart",
- "MoveForwardStop",
- "PetAssistMode",
- "PetAttack",
- "PetDefensiveAssistMode",
- "PetDefensiveMode",
- "PetFollow",
- "PetPassiveMode",
- "PetStopAttack",
- "PetWait",
- "PickupAction",
- "PickupCompanion",
- "PickupMacro",
- "PickupPetAction",
- "PickupSpell",
- "PickupSpellBookItem",
- "PitchDownStart",
- "PitchDownStop",
- "PitchUpStart",
- "PromoteToAssistant",
- "Quit",
- "Reload",
- "ReplaceEnchant",
- "ReplaceTradeEnchant",
- "RunMacro",
- "RunMacroText",
- "SendChatMessage",
- "SetBinding",
- "SetBindingClick",
- "SetBindingItem",
- "SetBindingMacro",
- "SetBindingSpell",
- "SetCurrentTitle",
- "SetMoveEnabled",
- "SetOverrideBinding",
- "SetOverrideBindingClick",
- "SetOverrideBindingItem",
- "SetOverrideBindingMacro",
- "SetOverrideBindingSpell",
- "SetPortraitTexture",
- "SetRaidTarget",
- "SetTurnEnabled",
- "ShowUIPanel",
- "SitStandOrDescendStart",
- "SpellCanTargetUnit",
- "SpellIsTargeting",
- "SpellStopCasting",
- "SpellStopTargeting",
- "SpellTargetItem",
- "SpellTargetUnit",
- "StartAttack",
- "StrafeLeftStart",
- "StrafeLeftStop",
- "StrafeRightStart",
- "StrafeRightStop",
- "Stuck",
- "SummonFriend",
- "SwapRaidSubgroup",
- "TargetLastEnemy",
- "TargetLastTarget",
- "TargetNearestEnemy",
- "TargetNearestFriend",
- "TargetUnit",
- "ToggleAutoRun",
- "ToggleGameMenu",
- "ToggleRun",
- "ToggleSpellAutocast",
- "TurnLeftStart",
- "TurnLeftStop",
- "TurnOrActionStart",
- "TurnOrActionStop",
- "TurnRightStart",
- "TurnRightStop",
- "UninviteUnit",
- "UnitAffectingCombat",
- "UnitArmor",
- "UnitAttackPower",
- "UnitAttackSpeed",
- "UnitAura",
- "UnitAuraSlots",
- "UnitBuff",
- "UnitCanAssist",
- "UnitCanAttack",
- "UnitCanCooperate",
- "UnitCastingInfo",
- "UnitChannelInfo",
- "UnitClass",
- "UnitClassification",
- "UnitCreatureFamily",
- "UnitCreatureType",
- "UnitDamage",
- "UnitDebuff",
- "UnitDetailedThreatSituation",
- "UnitExists",
- "UnitGetIncomingHeals",
- "UnitGetTotalHealAbsorbs",
- "UnitGroupRolesAssigned",
- "UnitGUID",
- "UnitHealth",
- "UnitHealthMax",
- "UnitInBattleground",
- "UnitInParty",
- "UnitInRaid",
- "UnitInRange",
- "UnitIsAFK",
- "UnitIsCharmed",
- "UnitIsConnected",
- "UnitIsCorpse",
- "UnitIsDead",
- "UnitIsDeadOrGhost",
- "UnitIsDND",
- "UnitIsEnemy",
- "UnitIsFeignDeath",
- "UnitIsFriend",
- "UnitIsGhost",
- "UnitIsInMyGuild",
- "UnitIsPlayer",
- "UnitIsPossessed",
- "UnitIsPVP",
- "UnitIsPVPFreeForAll",
- "UnitIsPVPSanctuary",
- "UnitIsSameServer",
- "UnitIsTrivial",
- "UnitIsUnit",
- "UnitIsVisible",
- "UnitLevel",
- "UnitName",
- "UnitOnTaxi",
- "UnitPhaseReason",
- "UnitPlayerControlled",
- "UnitPlayerOrPetInParty",
- "UnitPlayerOrPetInRaid",
- "UnitPower",
- "UnitPower",
- "UnitPowerMax",
- "UnitPowerType",
- "UnitPVPName",
- "UnitRace",
- "UnitRangedAttackPower",
- "UnitRangedDamage",
- "UnitReaction",
- "UnitSelectionColor",
- "UnitSex",
- "UnitStat",
- "UnitThreatSituation",
- "UnitUsingVehicle",
- "UnitXP",
- "UnitXPMax",
- "UseAction",
- "UseContainerItem",
- "UseInventoryItem",
- "UseItemByName",
- "UseToy",
- "UseToyByName"
-}
-
---------------------------------------------------------------------------------------------------------------------------------
--- functions exported to BadRotations
---------------------------------------------------------------------------------------------------------------------------------
-local _, br = ...
-local b = br._G
-local unlock = br.unlock
-local funcCopies = {}
-local ggUnlocked = false
-local gg = {}
-
--- helper function
-local function stringsplit(inputstr, sep)
- if sep == nil then
- sep = "%s"
- end
- local t = {}
- for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do
- table.insert(t, str)
- end
- return t
-end
-
-for i = 1, #unlockList do
- local func = unlockList[i]
- funcCopies[func] = _G[func]
-end
-
-
-function unlock.GGUnlock()
- if not ggUnlocked then
- if _G["GetExeDirectory"] == nil then return false end
- RunScript(ReadFile(GetExeDirectory().."\\api.lua"))
- if GetWowDirectory() ~= nil then
- -- b.print("Unlocked")
- ggUnlocked = true
- CreateApiReferences(gg)
- for k, v in pairs(gg) do
- b[k] = v
- end
- else
- -- b.print("Unlock Failed")
- return false
- end
- end
- --------------------------------
- -- API unlocking
- --------------------------------
- for k, v in pairs(funcCopies) do
- b[k] = function(...) return b.Unlock(v, ...) end
- end
-
- b.AuraUtil = {}
- local Aura = {}
- b.AuraUtil.FindAuraByName = function(...)
- if Aura ~= nil then table.wipe(Aura) end
- local spellName, unit, filter = ...
- if filter ~= nil then
- if _G.strfind(_G.strupper(filter), "HELPFUL") then
- for i = 1, 40 do
- local buffName, _, _, _, _, _, _, _, _, buffSpellID = b.UnitBuff(unit, i, "player")
- if buffName == nil then return nil end
- -- if buffSpellID == spellID then
- if buffName == spellName then
- return Aura
- end
- end
- else
- for i = 1, 40 do
- local buffName, _, _, _, _, _, _, _, _, buffSpellID = b.UnitDebuff(unit, i, "player")
- if buffName == nil then return nil end
- -- if buffSpellID == spellID then
- if buffName == spellName then
- return Aura
- end
- end
- end
- end
- return nil
- end
-
- local FieldType =
- {
- ["Bool"] = 1,
- ["Char"] = 2,
- ["Byte"] = 3,
- ["Short"] = 4,
- ["UShort"] = 5,
- ["Int"] = 6,
- ["UInt"] = 7,
- ["Long"] = 8,
- ["ULong"] = 9,
- ["Float"] = 10,
- ["Double"] = 11,
- ["StringType"] = 12,
- ["IntPtr"] = 13,
- ["UIntPtr"] = 14,
- ["GUID"] = 15,
- }
-
- --------------------------------
- -- API copy/rename/unlock
- --------------------------------
- b.GetWoWDirectory = function(...) return b.GetWowDirectory(...) end
- b.ObjectFacing = function(...) return b.UnitFacing(...) end
- b.UnitMovementFlags = function(...) return b.GetUnitMovementFlags(...) end
- b.CancelPendingSpell = function(...) return b.SpellStopTargeting(...) end
- b.GetMousePosition = function(...) return b.GetCursorPosition(...) end
- b.IsAoEPending = function(...) return b.SpellIsTargeting(...) end
- b.ObjectInteract = function(...) return b.InteractUnit(...) end
- b.ObjectIsVisible = function(...) return b.UnitIsVisible(...) end
-
- --------------------------------
- -- object fields
- --------------------------------
- b.UnitTarget = function(unit)
- return b.ObjectField(unit, 0x9C, FieldType.GUID) --b.ObjectField(unit, 0x1748, 15)
- end
- b.UnitCreator = function(unit)
- return b.ObjectField(unit, 0x6C, FieldType.GUID) --b.ObjectField(unit, 0x1718, 15)
- end
- b.UnitBoundingRadius = function(unit)
- return b.ObjectField(unit, 0x19C, FieldType.Float) --b.ObjectField(unit, 0x17DC, 10)
- end
- b.UnitCombatReach = function(unit)
- return b.ObjectField(unit, 0x1A0, FieldType.Float) --b.ObjectField(unit, 0x17E0, 10)
- end
- --------------------------------
- -- API conversions
- --------------------------------
- b.ObjectPointer = function(...)
- if b.UnitExists(...) then
- return b.UnitGUID(...)
- end
- end
- b.ObjectIsUnit = function(...)
- local ObjType = b.ObjectType(...)
- return ObjType == 5
- end
- b.ObjectID = function(object)
- return b.ObjectField(object, 0x10, FieldType.Int)
- -- local guid = b.UnitGUID(object)
- -- if guid then
- -- local _, _, _, _, _, objectId, _ = strsplit("-", guid);
- -- return tonumber(objectId);
- -- else
- -- return 0
- -- end
- end
- b.TraceLine = function(...)
- local hit, hitx, hity, hitz = gg.TraceLine(...)
- if hit == 1 then
- return hitx, hity, hitz
- else
- return nil
- end
- end
- b.UnitCastID = function(...)
- local spellId1 = select(9, b.UnitCastingInfo(...)) or 0
- local spellId2 = select(9, b.UnitChannelInfo(...)) or 0
- local castGUID = b.UnitTarget(select(1,...))
- return spellId1, spellId2, castGUID, castGUID
- end
- b.GetDirectoryFiles = function(...)
- local filter = stringsplit(..., "*")
- local files = gg.GetDirectoryFiles(filter[1], "*.lua")
- return stringsplit(files, "|")
- end
- b.WorldToScreen = function(...)
- local multiplier = GetDefaultScale() --UIParent:GetScale()
- local sX, sY = gg.WorldToScreen(...)
- return sX * multiplier, sY * multiplier * -1 + _G.WorldFrame:GetTop()
- -- local scale, x, y = UIParent:GetEffectiveScale(), gg.WorldToScreen(...)
- -- local sx = GetScreenWidth() * scale
- -- local sy = GetScreenHeight() * scale
- -- return x * sx, y * sy
- end
- b.FaceDirection = function(arg)
- if type(arg) == "number" then
- gg.FaceDirection(arg)
- else
- arg = b.GetAnglesBetweenObjects("player", arg)
- gg.FaceDirection(arg)
- end
- end
- b.GetObjectWithGUID = function(...)
- return ...
- end
- b.IsHackEnabled = function(...) return false end
- --------------------------------
- -- math
- --------------------------------
- b.GetDistanceBetweenPositions = function(X1, Y1, Z1, X2, Y2, Z2)
- return math.sqrt(math.pow(X2 - X1, 2) + math.pow(Y2 - Y1, 2) + math.pow(Z2 - Z1, 2))
- end
- b.GetAnglesBetweenObjects = function(Object1, Object2)
- if Object1 and Object2 then
- local X1, Y1, Z1 = b.ObjectPosition(Object1)
- local X2, Y2, Z2 = b.ObjectPosition(Object2)
- return math.atan2(Y2 - Y1, X2 - X1) % (math.pi * 2),
- math.atan((Z1 - Z2) / math.sqrt(math.pow(X1 - X2, 2) + math.pow(Y1 - Y2, 2))) % math.pi
- else
- return 0, 0
- end
- end
- b.GetAnglesBetweenPositions = function(X1, Y1, Z1, X2, Y2, Z2)
- return math.atan2(Y2 - Y1, X2 - X1) % (math.pi * 2),
- math.atan((Z1 - Z2) / math.sqrt(math.pow(X1 - X2, 2) + math.pow(Y1 - Y2, 2))) % math.pi
- end
- b.GetPositionFromPosition = function(X, Y, Z, Distance, AngleXY, AngleXYZ)
- return math.cos(AngleXY) * Distance + X, math.sin(AngleXY) * Distance + Y, math.sin(AngleXYZ) * Distance + Z
- end
- b.GetPositionBetweenPositions = function(X1, Y1, Z1, X2, Y2, Z2, DistanceFromPosition1)
- local AngleXY, AngleXYZ = b.GetAnglesBetweenPositions(X1, Y1, Z1, X2, Y2, Z2)
- return b.GetPositionFromPosition(X1, Y1, Z1, DistanceFromPosition1, AngleXY, AngleXYZ)
- end
- b.GetPositionBetweenObjects = function(unit1, unit2, DistanceFromPosition1)
- local X1, Y1, Z1 = b.ObjectPosition(unit1)
- local X2, Y2, Z2 = b.ObjectPosition(unit2)
- if not X1 or not X2 then return end
- local AngleXY, AngleXYZ = b.GetAnglesBetweenPositions(X1, Y1, Z1, X2, Y2, Z2)
- return b.GetPositionFromPosition(X1, Y1, Z1, DistanceFromPosition1, AngleXY, AngleXYZ)
- end
- b.GetDistanceBetweenObjects = function(unit1, unit2)
- local X1, Y1, Z1 = b.ObjectPosition(unit1)
- local X2, Y2, Z2 = b.ObjectPosition(unit2)
- return math.sqrt((X2-X1)^2 + (Y2-Y1)^2 + (Z2-Z1)^2)
- end
- b.ObjectIsFacing = function(obj1, obj2, degrees)
- local Facing = b.UnitFacing(obj1)
- local AngleToUnit = b.GetAnglesBetweenObjects(obj1, obj2)
- local AngleDifference = Facing > AngleToUnit and Facing - AngleToUnit or AngleToUnit - Facing
- local ShortestAngle = AngleDifference < math.pi and AngleDifference or math.pi * 2 - AngleDifference
- degrees = degrees and b.rad(degrees) / 2 or math.pi / 2
- return ShortestAngle < degrees
- end
- --------------------------------
- -- extra APIs
- --------------------------------
- b.ObjectIsGameObject = function(...)
- local ObjType = b.ObjectType(...)
- return ObjType == 8 or ObjType == 11
- end
- b.GetMapId = function()
- return select(8, GetInstanceInfo())
- end
- --------------------------------
- -- missing APIs
- --------------------------------
- b.IsQuestObject = function(obj)
- return false
- end
- b.ScreenToWorld = function()
- return 0, 0
- end
- b.UnitIsUnit = function(unit1, unit2)
- return b.UnitGUID(unit1) == b.UnitGUID(unit2)
- end
- br.unlocker = "GG"
- return true
-end
\ No newline at end of file
diff --git a/Unlockers/luabox.lua b/Unlockers/luabox.lua
deleted file mode 100644
index cb8d5a40ce..0000000000
--- a/Unlockers/luabox.lua
+++ /dev/null
@@ -1,449 +0,0 @@
-local _, br = ...
-local b = br._G
-local unlock = br.unlock
-local CurrentTable, OldTable
-local function copyTable(datatable)
- local tblRes = {}
- if type(datatable) == "table" then
- for k, v in pairs(datatable) do
- tblRes[copyTable(k)] = copyTable(v)
- end
- else
- tblRes = datatable
- end
- return tblRes
-end
-
-local TagHandlerList = {
- "IsSpellInRange",
- "IsItemInRange",
- "UnitInRange",
- "UnitAura",
- "UnitAuraSlots",
- "UnitPlayerControlled",
- "UnitIsVisible",
- "GetUnitSpeed",
- "UnitClass",
- "UnitIsTappedByPlayer",
- "UnitThreatSituation",
- "UnitCanAttack",
- "UnitCreatureType",
- "UnitIsDeadOrGhost",
- "UnitDetailedThreatSituation",
- "UnitIsUnit",
- "UnitHealthMax",
- "UnitAffectingCombat",
- "UnitIsPlayer",
- "UnitIsDead",
- "UnitInParty",
- "UnitInRaid",
- "UnitHealth",
- "UnitCastingInfo",
- "UnitChannelInfo",
- "UnitName",
- "UnitBuff",
- "UnitDebuff",
- -- "CanLootUnit",
- "UnitIsFriend",
- "UnitClassification",
- "UnitReaction",
- "UnitGroupRolesAssigned",
- -- "SetPortraitTexture",
- "UnitXPMax",
- "UnitXP",
- -- "UnitUsingVehicle",
- "UnitStat",
- -- "UnitSex",
- -- "UnitSelectionColor",
- "UnitPhaseReason",
- "UnitResistance",
- "UnitRangedDamage",
- "UnitRangedAttackPower",
- "UnitRangedAttack",
- "UnitRace",
- "UnitPowerType",
- "UnitPowerMax",
- "UnitPower",
- "UnitPVPName",
- "UnitPlayerOrPetInRaid",
- "UnitPlayerOrPetInParty",
- "UnitManaMax",
- "UnitMana",
- "UnitLevel",
- "UnitIsTrivial",
- "UnitIsTapped",
- "UnitIsSameServer",
- "UnitIsPossessed",
- -- "UnitIsPVPSanctuary",
- -- "UnitIsPVPFreeForAll",
- "UnitIsPVP",
- "UnitIsGhost",
- "UnitIsFeignDeath",
- "UnitIsEnemy",
- "UnitIsDND",
- "UnitIsCorpse",
- "UnitIsConnected",
- "UnitIsCharmed",
- -- "UnitIsAFK",
- -- "UnitIsInMyGuild",
- "UnitInBattleground",
- -- "GetPlayerInfoByGUID",
- -- "UnitDefense",
- "UnitDamage",
- "UnitCreatureFamily",
- "UnitCanCooperate",
- "UnitCanAssist",
- "UnitAttackSpeed",
- "UnitAttackPower",
- "UnitAttackBothHands",
- "UnitArmor",
- -- "InviteUnit",
- "GetUnitPitch",
- -- "GetUnitName",
- "FollowUnit",
- "CheckInteractDistance",
- "InitiateTrade",
- "UnitOnTaxi",
- "AssistUnit",
- "SpellTargetUnit",
- "CopyToClipboard",
- "SpellTargetItem",
- "SpellCanTargetUnit",
- -- "CombatTextSetActiveUnit",
- -- "SummonFriend",
- -- "CanSummonFriend",
- -- "GrantLevel",
- -- "CanGrantLevel",
- "SetRaidTarget",
- -- "GetReadyCheckStatus",
- "GetRaidTargetIndex",
- -- "GetPartyAssignment",
- -- "DemoteAssistant",
- -- "PromoteToAssistant",
- "IsUnitOnQuest",
- "DropItemOnUnit",
- "GetDefaultLanguage",
- "GetCritChanceFromAgility",
- "GetSpellCritChanceFromIntellect",
- "UnitGetTotalHealAbsorbs",
- "UnitGetIncomingHeals",
- "CastSpellByName",
- "CastSpellByID",
- "UseItemByName",
- "SpellIsTargeting",
- "InteractUnit",
- "CancelUnitBuff",
- "TargetUnit",
- "UnitGUID",
- "UnitExists"
-}
-local UnlockList = {
- "ToggleGameMenu",
- "RunMacroText",
- "UseInventoryItem",
- "SpellStopCasting",
- "CameraOrSelectOrMoveStart",
- "CameraOrSelectOrMoveStop",
- "CancelShapeshiftForm",
- "PetAssistMode",
- "PetPassiveMode",
- "SpellStopTargeting",
- "AscendStop",
- "JumpOrAscendStart",
- "JumpOrAscendStop",
- "MoveBackwardStart",
- "MoveBackwardStop",
- "MoveForwardStart",
- "StrafeLeftStart",
- "StrafeLeftStop",
- "StrafeRightStart",
- "StrafeRightStop",
- "TurnLeftStart",
- "TurnLeftStop",
- "TurnRightStart",
- "TurnRightStop",
- "PitchUpStart",
- "PitchDownStart",
- "PitchDownStop",
- "ClearTarget",
- "AcceptProposal",
- "CastPetAction",
- "CastShapeshiftForm",
- "CastSpell",
- "ChangeActionBarPage",
- "ClearOverrideBindings",
- "CreateMacro",
- "DeleteCursorItem",
- "DeleteMacro",
- "DescendStop",
- "DestroyTotem",
- "FocusUnit",
- "ForceQuit",
- "GetUnscaledFrameRect",
- "GuildControlSetRank",
- "GuildControlSetRankFlag",
- "GuildDemote",
- "GuildPromote",
- "GuildUninvite",
- "JoinBattlefield",
- "Logout",
- "PetAttack",
- "PetDefensiveAssistMode",
- "PetDefensiveMode",
- "PetFollow",
- "PetStopAttack",
- "PetWait",
- "PickupAction",
- "PickupCompanion",
- "PickupMacro",
- "PickupPetAction",
- "PickupSpell",
- "PickupSpellBookItem",
- "Quit",
- "Region_GetBottom",
- "Region_GetCenter",
- "Region_GetPoint",
- "Region_GetRect",
- "Region_Hide",
- "Region_SetPoint",
- "Region_Show",
- "RegisterForSave",
- "ReplaceEnchant",
- "ReplaceTradeEnchant",
- "RunMacro",
- "SendChatMessage",
- "SetBinding",
- "SetBindingClick",
- "SetBindingItem",
- "SetBindingMacro",
- "SetBindingSpell",
- "SetCurrentTitle",
- "SetMoveEnabled",
- "SetOverrideBinding",
- "SetOverrideBindingClick",
- "SetOverrideBindingItem",
- "SetOverrideBindingMacro",
- "SetOverrideBindingSpell",
- "SetTurnEnabled",
- "ShowUIPanel",
- "SitStandOrDescendStart",
- -- "Stuck",
- -- "SwapRaidSubgroup",
- "TargetLastEnemy",
- "TargetLastTarget",
- "TargetNearestEnemy",
- "TargetNearestFriend",
- -- "ToggleAutoRun",
- -- "ToggleRun",
- "TurnOrActionStart",
- "TurnOrActionStop",
- -- "UIObject_SetForbidden",
- -- "UninviteUnit",
- "UseAction",
- -- "UseContainerItem",
- "UseToy",
- "UseToyByName",
- -- "AcceptBattlefieldPort",
- "AcceptTrade",
- "AttackTarget",
- -- "CancelItemTempEnchantment",
- "CancelLogout",
- "StartAttack"
-}
-function unlock.LBUnlock()
- local unlocked = false
- local lb = _G.__LB__
- for _, val in ipairs(TagHandlerList) do
- for _, rot in ipairs(UnlockList) do
- if val == rot then
- print(rot)
- end
- end
- end
- local function lbUnlock(method)
- b[method] = function(...)
- return lb.Unlock(_G[method], ...)
- end
- end
- local function lbUnitTagHandler(method)
- b[method] = function(...)
- return lb.UnitTagHandler(lb.Unlock, _G[method], ...)
- end
- end
- for _, method in ipairs(TagHandlerList) do
- lbUnitTagHandler(method)
- end
- for _, method in ipairs(UnlockList) do
- lbUnlock(method)
- end
- b.GetCameraPosition = function(...) return lb.GetCameraPosition(...) end
- b.AuraUtil = {}
- b.AuraUtil.FindAuraByName = function(...)
- return lb.UnitTagHandler(lb.Unlock, _G.AuraUtil["FindAuraByName"], ...)
- end
- b.ObjectPointer = b.UnitGUID --compatibility change as LB returns GUIDs instead of Pointers in their OM
- b.ObjectPosition = function(...)
- return lb.ObjectPosition(...)
- end
- b.ObjectGUID = br._G.UnitGUID
- b.ObjectIsUnit = function(...)
- local ObjType = lb.ObjectType(...)
- return ObjType == 5 or ObjType == 6 or ObjType == 7
- end
- b.ObjectIsGameObject = function(...)
- local ObjType = lb.ObjectType(...)
- return ObjType == 8 or ObjType == 11
- end
- b.ObjectID = function(...)
- return lb.ObjectId(...)
- end
- b.UnitMovementFlags = function(...)
- return lb.UnitMovementFlags(...)
- end
- b.TraceLine = function(...)
- return lb.Raycast(...)
- end
- b.UnitTarget = lb.UnitTarget
- b.IsQuestObject = function(obj)
- return lb.ObjectHasDynamicFlag(obj, lb.EUnitDynamicFlags.SpecialInfo)
- end
- b.UnitCastID = function(...)
- local CastSpellID, CastTargetGUID, timeLeft, NotInterruptible = lb.UnitCastingInfo(...)
- local ChannelSpellID, ChannelTargetGUID, timeLeft, NotInterruptible = lb.UnitChannelInfo(...)
- return CastSpellID, ChannelSpellID, CastTargetGUID, ChannelTargetGUID
- end
- b.GetWoWDirectory = lb.GetGameDirectory
- b.CreateDirectory = lb.CreateDirectory
- b.GetDirectoryFiles = lb.GetFiles
- b.GetKeyState = lb.GetKeyState
- b.WorldToScreen = function (...)
- local ResolutionCoef = WorldFrame:GetWidth() / lb.GetWindowSize()
- local sX, sY = lb.WorldToScreen(...)
- if sX and sY then
- return sX * ResolutionCoef, -sY * ResolutionCoef
- else
- return sX, sY
- end
- end
- b.ScreenToWorld = function()
- return 0, 0
- end
- b.WorldToScreenRaw = function(...)
- local x, y = lb.WorldToScreen(...)
- return x, y
- end
- b.GetDistanceBetweenPositions = function(X1, Y1, Z1, X2, Y2, Z2)
- return math.sqrt(math.pow(X2 - X1, 2) + math.pow(Y2 - Y1, 2) + math.pow(Z2 - Z1, 2))
- end
-
- b.GetAnglesBetweenObjects = function(Object1, Object2)
- local X1, Y1, Z1 = br._G.ObjectPosition(Object1)
- local X2, Y2, Z2 = br._G.ObjectPosition(Object2)
- -- print(Unit1,X1,Y1,Z1,unit2,X2,Y2,Z2)
- return math.atan2(Y2 - Y1, X2 - X1) % (math.pi * 2), math.atan((Z1 - Z2) / math.sqrt(math.pow(X1 - X2, 2) + math.pow(Y1 - Y2, 2))) % math.pi
- end
-
- b.GetAnglesBetweenPositions = function(X1, Y1, Z1, X2, Y2, Z2)
- return math.atan2(Y2 - Y1, X2 - X1) % (math.pi * 2), math.atan((Z1 - Z2) / math.sqrt(math.pow(X1 - X2, 2) + math.pow(Y1 - Y2, 2))) % math.pi
- end
-
- b.GetPositionFromPosition = function(X, Y, Z, Distance, AngleXY, AngleXYZ)
- return math.cos(AngleXY) * Distance + X, math.sin(AngleXY) * Distance + Y, math.sin(AngleXYZ) * Distance + Z
- end
-
- b.GetPositionBetweenPositions = function(X1, Y1, Z1, X2, Y2, Z2, DistanceFromPosition1)
- local AngleXY, AngleXYZ = b.GetAnglesBetweenPositions(X1, Y1, Z1, X2, Y2, Z2)
- return b.GetPositionFromPosition(X1, Y1, Z1, DistanceFromPosition1, AngleXY, AngleXYZ)
- end
-
- b.GetPositionBetweenObjects = function(unit1, unit2, DistanceFromPosition1)
- local X1, Y1, Z1 = br._G.ObjectPosition(unit1)
-
- local X2, Y2, Z2 = br._G.ObjectPosition(unit2)
- local AngleXY, AngleXYZ = b.GetAnglesBetweenPositions(X1, Y1, Z1, X2, Y2, Z2)
- return b.GetPositionFromPosition(X1, Y1, Z1, DistanceFromPosition1, AngleXY, AngleXYZ)
- end
- b.ObjectFacing = lb.ObjectFacing
- b.FaceDirection = function(arg)
- if type(arg) == "number" then
- lb.SetPlayerAngles(arg)
- else
- arg = b.GetAnglesBetweenObjects("player", arg)
- lb.SetPlayerAngles(arg)
- end
- end
- b.ObjectIsFacing = function(obj1, obj2, degrees)
- local Facing = lb.ObjectFacing(obj1)
- local AngleToUnit = b.GetAnglesBetweenObjects(obj1, obj2)
- local AngleDifference = Facing > AngleToUnit and Facing - AngleToUnit or AngleToUnit - Facing
- local ShortestAngle = AngleDifference < math.pi and AngleDifference or math.pi * 2 - AngleDifference
- degrees = degrees and br._G.rad(degrees) / 2 or math.pi / 2
- return ShortestAngle < degrees
- end
- b.ObjectInteract = b.InteractUnit
- -- br.getFacing = ObjectFacingObject
- b.UnitCreator = lb.ObjectCreator
- b.ObjectName = lb.ObjectName
- b.GetDistanceBetweenObjects = function(...) return lb.GetDistance3D(...) end
- b.GetMapId = lb.GetMapId
- b.UnitIsMounted = function(...)
- return lb.UnitHasFlag(..., lb.EUnitFlags.Mount)
- end
- b.SendMovementUpdate = lb.UpdatePlayerMovement
-
- b.ObjectDynamicFlags = lb.ObjectDynamicFlags
-
- b.IsHackEnabled = function(...)
- --print(...)
- return false
- end
- b.UnitCombatReach = lb.UnitCombatReach
- b.ReadFile = lb.ReadFile
- b.DirectoryExists = lb.DirectoryExists
- b.WriteFile = function(...)
- return lb.WriteFile(...)
- end
- -- local addedOM,removedOM = {}, {}
- b.GetObjectCount = function()
- if not OldTable and not CurrentTable then
- CurrentTable = lb.GetObjects()
- return #CurrentTable, true, CurrentTable, {}
- else
- OldTable = CurrentTable
- CurrentTable = lb.GetObjects()
- local TempTable = copyTable(CurrentTable)
- local TempTableOld = copyTable(OldTable)
- for i = #TempTableOld, 1, -1 do
- for k = #TempTable, 1, -1 do
- if TempTableOld[i] == TempTable[k] then
- table.remove(TempTable, k)
- table.remove(TempTableOld, i)
- break
- end
- end
- end
- return #CurrentTable, true, TempTable, TempTableOld
- end
- end
- b.GetObjectWithIndex = function(...)
- return CurrentTable[...]
- end
- b.GetObjectWithGUID = function(...)
- return ...
- end
- b.GetMousePosition = function()
- local cur_x, cur_y = br._G.GetCursorPosition()
- return cur_x, cur_y
- end
- b.ObjectIsVisible = lb.ObjectExists
- b.ObjectExists = lb.ObjectExists
- -- br.GetUnitIsVisible = lb.ObjectExists
- b.IsAoEPending = lb.IsAoEPending
- b.ClickPosition = function(...)
- return lb.Unlock(lb.ClickPosition, ...) end
- b.UnitBoundingRadius = lb.UnitBoundingRadius
- b.CancelPendingSpell = lb.CancelPendingSpells
- br.unlocker = "LuaBox"
- unlocked = true
- return unlocked
-end
diff --git a/Unlockers/minibot.lua b/Unlockers/minibot.lua
deleted file mode 100644
index 5d82eeb571..0000000000
--- a/Unlockers/minibot.lua
+++ /dev/null
@@ -1,233 +0,0 @@
-local _, br = ...
-local b = br._G
-local unlock = br.unlock
--- Minibot
-function unlock.MBUnlock()
- local wmbapi = _G.wmbapi
- local unlocked = false
- -- Active Player
- b.StopFalling = wmbapi.StopFalling
- b.GetAnglesBetweenObjects = function(obj1, obj2)
- if b.UnitIsVisible(obj1) and b.UnitIsVisible(obj2) then
- return wmbapi.GetAnglesBetweenObjects(obj1, obj2)
- else
- return 0, 0
- end
- end
- b.FaceDirection = function(a)
- if wmbapi.GetObject(a) then
- wmbapi.FaceDirection(b.GetAnglesBetweenObjects(a, "player"), true)
- else
- wmbapi.FaceDirection(a, true)
- end
- end
- -- Object
- b.ObjectTypeFlags = wmbapi.ObjectTypeFlags
- b.ObjectPointer = function(obj)
- if b.UnitIsVisible(obj) then
- return wmbapi.GetObject(obj)
- else
- return ""
- end
- end
- b.ObjectExists = wmbapi.ObjectExists
- b.ObjectIsVisible = b.UnitIsVisible
- b.ObjectPosition = function(obj)
- local x, y, z = wmbapi.ObjectPosition(obj)
- if x then
- return x, y, z
- else
- return 0, 0, 0
- end
- end
- b.ObjectFacing = function(obj)
- if b.UnitIsVisible(obj) then
- return wmbapi.ObjectFacing(obj)
- else
- return 0
- end
- end
- b.ObjectName = function(obj)
- if b.ObjectExists(obj) then
- return b.UnitName(obj)
- else
- return "Unknown"
- end
- end
- b.ObjectID = function(obj)
- if b.UnitIsVisible(obj) then
- return wmbapi.ObjectId(obj)
- else
- return 0
- end
- end
- b.ObjectDescriptor = function(obj, offset, type)
- return b.UnitIsVisible(obj) and wmbapi.ObjectDescriptor(obj, offset, type)
- end
- b.ObjectIsUnit = function(obj)
- return b.UnitIsVisible(obj) and wmbapi.ObjectIsType(obj, wmbapi.GetObjectTypeFlagsTable().Unit)
- end
- b.GetDistanceBetweenPositions = function(...)
- return (... and wmbapi.GetDistanceBetweenPositions(...)) or 0
- end
- b.GetDistanceBetweenObjects = function(obj1, obj2)
- if b.UnitIsVisible(obj1) and b.UnitIsVisible(obj2) then
- return wmbapi.GetDistanceBetweenObjects(obj1, obj2)
- else
- return 0
- end
- end
- b.GetPositionBetweenObjects = function(obj1, obj2, dist)
- if b.UnitIsVisible(obj1) and b.UnitIsVisible(obj2) then
- return wmbapi.GetPositionBetweenObjects(obj1, obj2, dist)
- else
- return 0, 0, 0
- end
- end
- b.GetPositionFromPosition = function(...)
- return (... and wmbapi.GetPositionFromPosition(...)) or 0, 0, 0
- end
- b.ObjectIsFacing = function(obj1, obj2, toler)
- if b.UnitIsVisible(obj1) and b.UnitIsVisible(obj2) then
- return (toler and wmbapi.ObjectIsFacing(obj1, obj2, toler)) or (not toler and wmbapi.ObjectIsFacing(obj1, obj2))
- end
- end
- b.ObjectInteract = b.InteractUnit
- --------------------------------
- -- internal unit manager
- --------------------------------
- local const_updateObjectListTickDelay = 10
- local g_lastKnownObjectList = {}
- local g_lastObjectCount = nil
- local g_lastObjectGuid = nil
- local g_lastUpdateTick = 0
-
- b.GetObjectCount = wmbapi.GetObjectCount
-
- -- b.GetNewObjects = function()
- -- local added, removed = {}, {}
- -- added, removed = select(3, wmbapi.GetObjectCount()), select(4, wmbapi.GetObjectCount())
- -- return added, removed
- -- end
-
- b.GetNpcCount = wmbapi.GetNpcCount
- b.GetPlayerCount = wmbapi.GetPlayerCount
- b.GetObjectWithIndex = wmbapi.GetObjectWithIndex
- b.GetNpcWithIndex = wmbapi.GetNpcWithIndex
- b.GetPlayerWithIndex = wmbapi.GetPlayerWithIndex
- b.GetObjectWithGUID = function(GUID)
- if GUID and #GUID > 1 then
- return wmbapi.GetObjectWithGUID(GUID)
- else
- return ""
- end
- end
- -- Unit
- b.UnitBoundingRadius = function(obj)
- if b.UnitIsVisible(obj) then
- return wmbapi.UnitBoundingRadius(obj)
- else
- return 0
- end
- end
- b.UnitCombatReach = function(obj)
- if b.UnitIsVisible(obj) then
- return wmbapi.UnitCombatReach(obj)
- else
- return 0
- end
- end
- b.UnitTarget = function(obj)
- if b.UnitIsVisible(obj) then
- return wmbapi.UnitTarget(obj)
- else
- return ""
- end
- end
- b.UnitCastID = function(obj)
- if b.UnitIsVisible(obj) then
- local spellId, target = wmbapi.UnitCasting(obj)
- return spellId or 0, spellId or 0, target or "", target or ""
- else
- return 0, 0, "", ""
- end
- end
- b.UnitCreator = function(obj)
- if b.UnitIsVisible(obj) then
- return wmbapi.UnitCreator(obj)
- else
- return ""
- end
- end
- -- World
- b.TraceLine = wmbapi.TraceLine
- b.GetCameraPosition = wmbapi.GetCameraPosition
- b.CancelPendingSpell = wmbapi.CancelPendingSpell
- b.ClickPosition = wmbapi.ClickPosition
- b.IsAoEPending = wmbapi.IsAoEPending
- b.GetTargetingSpell = wmbapi.IsAoEPending
- b.WorldToScreen = function(...)
- local scale, x, y = UIParent:GetEffectiveScale(), select(2, wmbapi.WorldToScreen(...))
- local sx = GetScreenWidth() * scale
- local sy = GetScreenHeight() * scale
- return x * sx, y * sy
- end
- b.ScreenToWorld = function(X, Y)
- local scale = UIParent:GetEffectiveScale()
- local sx = GetScreenWidth() * scale
- local sy = GetScreenHeight() * scale
- return wmbapi.ScreenToWorld(X / sx, Y / sy)
- end
- b.GetMousePosition = function()
- local cur_x, cur_y = b.GetCursorPosition()
- return cur_x, cur_y
- end
- -- Hacks
- b.IsHackEnabled = function()
- return
- end
- b.SetHackEnabled = function()
- return true
- end
- -- Files
- b.GetDirectoryFiles = wmbapi.GetDirectoryFiles
- b.ReadFile = wmbapi.ReadFile
- b.WriteFile = wmbapi.WriteFile
- b.CreateDirectory = wmbapi.CreateDirectory
- b.GetWoWDirectory = wmbapi.GetWoWDirectory
- b.DirectoryExists = wmbapi.DirectoryExists
- -- Callbacks
- b.AddEventCallback = function(Event, Callback)
- if not b.BRFrames then
- b.BRFrames = b.CreateFrame("Frame")
- b.BRFrames:SetScript("OnEvent", b.BRFrames_OnEvent)
- end
- b.BRFrames:RegisterEvent(Event)
- if not b.BRFramesEvent[Event] then
- b.BRFramesEvent[Event] = Callback
- end
- end
- -- Misc
- b.SendHTTPRequest = wmbapi.SendHttpRequest
- b.GetKeyState = wmbapi.GetKeyState
- br.Offsets = {
- ["cggameobjectdata__flags"] = "CGGameObjectData__Flags",
- }
- b.GetOffset = function(offset)
- return wmbapi.GetObjectDescriptorsTable()[br.Offsets[string.lower(offset)]]
- end
- -- Drawing
- b.GetWoWWindow = b.GetPhysicalScreenSize
- b.Draw2DLine = _G.LibStub("LibDraw-BR").Draw2DLine
- b.Draw2DText = _G.LibStub("LibDraw-BR").Text
- b.WorldToScreenRaw = function(...)
- local x, y = select(2, wmbapi.WorldToScreen(...))
- return x, 1 - y
- end
- b.IsQuestObject = function(...)
- return wmbapi.ObjectIsQuestObjective(...,false)
- end
- br.unlocker = "Minibot"
- unlocked = true
- return unlocked
-end
diff --git a/Unlockers/mmb.lua b/Unlockers/mmb.lua
deleted file mode 100644
index 96d7c3af89..0000000000
--- a/Unlockers/mmb.lua
+++ /dev/null
@@ -1,3 +0,0 @@
------------------------------------------------------------------------------------------------------------------------------
---- DUMMY FILE! Replace with file from #important-announcements in BR Discord or #files under BADROTATIONS in MMB Discord ---
------------------------------------------------------------------------------------------------------------------------------
\ No newline at end of file
diff --git a/Unlockers/nn.lua b/Unlockers/nn.lua
index 98c0ae5d8f..11197f3349 100644
--- a/Unlockers/nn.lua
+++ b/Unlockers/nn.lua
@@ -30,6 +30,7 @@ local unlockList =
"DestroyTotem",
"FocusUnit",
"ForceQuit",
+ "GetComboPoints",
"GetUnscaledFrameRect",
"GuildControlSetRank",
"GuildControlSetRankFlag",
@@ -205,7 +206,8 @@ local globalCacheList =
"UnitLevel",
"UnitName",
"UnitOnTaxi",
- "UnitPhaseReason",
+ -- "UnitPhaseReason",
+ "UnitInPhase",
"UnitPlayerControlled",
"UnitPlayerOrPetInParty",
"UnitPlayerOrPetInRaid",
@@ -226,7 +228,7 @@ local globalCacheList =
"UnitXP",
"UnitXPMax",
"UseInventoryItem",
- "WorldToScreen",
+ -- "WorldToScreen",
"WriteFile"
}
@@ -234,7 +236,7 @@ local globalCacheList =
--------------------------------------------------------------------------------------------------------------------------------
-- functions exported to BadRotations
--------------------------------------------------------------------------------------------------------------------------------
-local _, br = ...
+local Nn, br = ...
local b = br._G
local funcCopies = {}
local globalFuncCopies = {}
@@ -278,7 +280,7 @@ for i = 1, #globalCacheList do
end
-- print("NN File Called")
-function br.unlock:NNUnlock()
+function br.unlockers:NNUnlock()
if not C_Timer.Nn then return false end
setfenv(1, C_Timer.Nn)
-- print("NN Api Loaded")
@@ -317,7 +319,7 @@ function br.unlock:NNUnlock()
b.ObjectFacing = ObjectFacing
b.ObjectExists = ObjectExists
b.GetCameraPosition = GetCameraPosition
- b.UnitFacing = ObjectFacing
+ b.UnitFacing = UnitFacing
b.GetMousePosition = b.GetCursorPosition
b.CancelPendingSpell = b.SpellStopTargeting
b.ObjectIsVisible = b.UnitIsVisible
@@ -374,8 +376,11 @@ function br.unlock:NNUnlock()
local om = {}
b.GetObjectCount = function()
- om = Objects()
- return #Objects()
+ -- Call Objects() exactly once: the snapshot used for GetObjectWithIndex must match
+ -- the count returned, otherwise index overruns return nil under heavy load.
+ local objects = Objects()
+ om = objects
+ return #objects
end
b.GetObjectWithIndex = function(index)
@@ -400,7 +405,7 @@ function br.unlock:NNUnlock()
str = str .. "*.lua" --:match("*.lua") or str
local filter = str:gsub(str:match("*.lua"), "*")
-- print("Filter: "..filter)
- local files = ListFiles(filter)
+ local files = ListFiles(filter) or {}
local returnFiles = ""
for i = 1, #files do
-- print("File: "..files[i])
@@ -425,7 +430,7 @@ function br.unlock:NNUnlock()
end
b.GetObjectWithGUID = function(...)
- return ...
+ return Object(...)
end
b.IsHackEnabled = function(...) return false end
@@ -505,6 +510,7 @@ function br.unlock:NNUnlock()
b.GetMapId = function()
return select(8, b.GetInstanceInfo())
end
+ -- b.WorldToScreen = Nn.WorldToScreen
--------------------------------
-- missing APIs
@@ -513,6 +519,7 @@ function br.unlock:NNUnlock()
return false
end
- br.unlocker = "NN"
+ br.unlockers.selected = "NN"
+ b.print("NN Unlocker Loaded")
return true
end
diff --git a/Unlockers/tinkr.lua b/Unlockers/tinkr.lua
index 990b5b7a91..727e7aa3e6 100644
--- a/Unlockers/tinkr.lua
+++ b/Unlockers/tinkr.lua
@@ -1,3 +1,5 @@
+---@diagnostic disable: undefined-global
+-- req.saved.disableAutoTargeting = true
--------------------------------------------------------------------------------------------------------------------------------
-- unlockList
--------------------------------------------------------------------------------------------------------------------------------
@@ -218,7 +220,7 @@ local unlockList =
--------------------------------------------------------------------------------------------------------------------------------
local _, br = ...
local b = br._G
-local unlock = br.unlock
+local unlock = br.unlockers
local tinkrUnlocked = false
local funcCopies = {}
@@ -255,15 +257,15 @@ function unlock.TinkrUnlock()
--------------------------------
-- API unlocking
--------------------------------
- for k, v in pairs(funcCopies) do
- b[k] = function(...)
- if select('#', ...) == 0 then
- return Eval(k .. "()", "")
- else
- return Eval(k .. "(" .. table.concat({ ... }, ", ") .. ")", "")
- end
- end
- end
+ -- for k, v in pairs(funcCopies) do
+ -- b[k] = function(...)
+ -- if select('#', ...) == 0 then
+ -- return Eval(k .. "()", "")
+ -- else
+ -- return Eval(k .. "(" .. table.concat({ ... }, ", ") .. ")", "")
+ -- end
+ -- end
+ -- end
-------------------
-- API Wrapping ---
@@ -271,27 +273,34 @@ function unlock.TinkrUnlock()
------------------------- Active Player -------------------
b.FaceDirection = function(arg)
if type(arg) == "number" then
- FaceDirection(arg, true)
+ SetHeading(arg)
else
arg = b.GetAnglesBetweenObjects("player", arg)
- FaceDirection(arg, true)
+ SetHeading(arg)
end
end
b.GetMapId = GetMapID
------------------------- Object --------------------------
- b.ObjectPointer = ObjectGUID
+ b.Object = function(...) return Object(...) end
+ b.ObjectPointer = function(...) return Object(...) end
b.ObjectExists = function(...) return Object(...) ~= nil end
b.ObjectIsVisible = function(...) return Object(...) ~= nil end
- b.ObjectPosition = ObjectPosition
+ b.ObjectPosition = function(...)
+ if ObjectMover(...) then return ObjectWorldPosition(...) end
+ return ObjectPosition(...)
+ end
b.ObjectRawPosition = ObjectRawPosition
b.MoveToRaw = MoveToRaw
- b.ObjectFacing = ObjectRotation
- b.ObjectGUID = ObjectGUID
- b.ObjectName = ObjectName
- b.ObjectID = ObjectId
- b.ObjectType = ObjectType
- b.ObjectRawType = GameObjectType
+ b.ObjectFacing = function(...)
+ if ObjectMover(...) then return ObjectRawRotation(...) end
+ return ObjectRotation(...)
+ end
+ b.ObjectGUID = function(...) return ObjectGUID(Object(...)) end
+ b.ObjectName = function(...) return ObjectName(Object(...)) end
+ b.ObjectID = function(...) return ObjectID(...) end
+ b.ObjectType = function(...) return ObjectType(Object(...)) end
+ b.ObjectRawType = function(...) return GameObjectType(Object(...)) end
b.ObjectIsUnit = function(...)
local ObjType = ObjectType(...)
return ObjType == 5
@@ -301,6 +310,8 @@ function unlock.TinkrUnlock()
return FastDistance(X1, Y1, Z1, X2, Y2, Z2)
end
b.GetAnglesBetweenObjects = function(Object1, Object2)
+ Object1 = Object(Object1)
+ Object2 = Object(Object2)
if Object1 and Object2 then
local X1, Y1, Z1 = b.ObjectPosition(Object1)
local X2, Y2, Z2 = b.ObjectPosition(Object2)
@@ -322,6 +333,8 @@ function unlock.TinkrUnlock()
return b.GetPositionFromPosition(X1, Y1, Z1, DistanceFromPosition1, AngleXY, AngleXYZ)
end
b.GetPositionBetweenObjects = function(unit1, unit2, DistanceFromPosition1)
+ unit1 = Object(unit1)
+ unit2 = Object(unit2)
local X1, Y1, Z1 = b.ObjectPosition(unit1)
local X2, Y2, Z2 = b.ObjectPosition(unit2)
if not X1 or not X2 then return end
@@ -332,9 +345,13 @@ function unlock.TinkrUnlock()
-- local X1, Y1, Z1 = b.ObjectPosition(unit1)
-- local X2, Y2, Z2 = b.ObjectPosition(unit2)
-- return math.sqrt((X2-X1)^2 + (Y2-Y1)^2 + (Z2-Z1)^2)
+ unit1 = Object(unit1)
+ unit2 = Object(unit2)
return ObjectDistance(unit1, unit2)
end
b.ObjectIsFacing = function(obj1, obj2, degrees)
+ obj1 = Object(obj1)
+ obj2 = Object(obj2)
local Facing = b.UnitFacing(obj1)
local AngleToUnit = b.GetAnglesBetweenObjects(obj1, obj2)
local AngleDifference = Facing > AngleToUnit and Facing - AngleToUnit or AngleToUnit - Facing
@@ -349,22 +366,26 @@ function unlock.TinkrUnlock()
local om = {}
b.GetObjectCount = function()
table.wipe(om)
- om = Objects()
- return #Objects()
+ -- om = Objects()
+ local objects = Objects()
+ for i, object in ipairs(objects) do
+ om[i] = object
+ end
+ return #om--Objects()
end
b.GetObjectWithIndex = function(index)
return tostring(om[index])
end
b.GetObjectWithGUID = function(...)
- return ...
+ return Object(...)
end
------------------------- Unit ------------------
- b.UnitCreator = ObjectCreator
- b.UnitMovementFlags = ObjectMovementFlag
- b.UnitBoundingRadius = ObjectBoundingRadius
- b.UnitCombatReach = ObjectCombatReach
- b.UnitTarget = ObjectTarget
- b.UnitCastID = ObjectCastingInfo
+ b.UnitCreator = function(...) return ObjectCreator(Object(...)) end
+ b.UnitMovementFlags = function(...) return ObjectMovementFlag(Object(...)) end
+ b.UnitBoundingRadius = function(...) return ObjectBoundingRadius(Object(...)) end
+ b.UnitCombatReach = function(...) return ObjectCombatReach(Object(...)) end
+ b.UnitTarget = function(...) return ObjectCastingTarget(Object(...)) end
+ b.UnitCastID = function(...) return ObjectCastingInfo(Object(...)) end
------------------------- World ---------------------------
b.TraceLine = TraceLine
b.GetCameraPosition = CameraPosition
@@ -422,11 +443,11 @@ function unlock.TinkrUnlock()
b.ObjectInteract = b.InteractUnit
b.IsHackEnabled = function(...) return false end
- b.AuraUtil = {}
- b.AuraUtil.FindAuraByName = function(name, unit, filter)
- -- return Eval("AuraUtil.FindAuraByName("..table.concat({...}, ", ")..")", "")
- return AuraUtil.FindAuraByName(name, ObjectUnit(unit), filter)
- end
+ -- b.AuraUtil = {}
+ -- b.AuraUtil.FindAuraByName = function(name, unit, filter)
+ -- -- return Eval("AuraUtil.FindAuraByName("..table.concat({...}, ", ")..")", "")
+ -- return AuraUtil.FindAuraByName(name, Object(unit), filter)
+ -- end
b.ObjectIsGameObject = function(...)
local ObjType = ObjectType(...)
return ObjType == 8 or ObjType == 11
@@ -439,14 +460,14 @@ function unlock.TinkrUnlock()
end
b.TargetUnit = function(unit)
if Object(unit) then
- return TargetUnit(Object(unit):unit())
+ return TargetUnit(Object(unit))
else
- return
+ return nil
end
end
b.InteractUnit = function(unit)
if Object(unit) then
- return InteractUnit(Object(unit):unit())
+ return InteractUnit(Object(unit))
else
return
end
@@ -455,164 +476,178 @@ function unlock.TinkrUnlock()
--- API - Unit Function Object Handler ---
------------------------------------------
b.CastSpellByName = function(spell, unit)
- return Eval("CastSpellByName(\"" .. spell .. "\", \"" .. ObjectUnit(unit) .. "\")", "")
- end
- b.GetRaidTargetIndex = function(...)
- return GetRaidTargetIndex(ObjectUnit(...))
- end
- b.GetUnitSpeed = function(...)
- return GetUnitSpeed(ObjectUnit(...))
- end
- b.InSpellInRange = function(spell, unit)
- return C_Spell.IsSpellInRange(spell, ObjectUnit(unit))
- end
- b.UnitAffectingCombat = function(...)
- return UnitAffectingCombat(ObjectUnit(...))
- end
- b.UnitAttackSpeed = function(...)
- return UnitAttackSpeed(ObjectUnit(...))
- end
- b.UnitAura = function(unit, index, filter)
- local unpack = unpack
- local function UnpackAuraData(auraData)
- if not auraData then
- return nil
- end
- return auraData.name,
- auraData.icon,
- auraData.applications,
- auraData.dispelName,
- auraData.duration,
- auraData.expirationTime,
- auraData.sourceUnit,
- auraData.isStealable,
- auraData.nameplateShowPersonal,
- auraData.spellId,
- auraData.canApplyAura,
- auraData.isBossAura,
- auraData.isFromPlayerOrPlayerPet,
- auraData.nameplateShowAll,
- auraData.timeMod,
- unpack(auraData.points)
+ -- return Eval("CastSpellByName(\"" .. spell .. "\", \"" .. Object(unit) .. "\")", "")
+ if Object(unit) then
+ print("CastSpellByName: Passed " .. tostring(unit) .. " with object ref " .. tostring(Object(unit)))
+ return CastSpellByName(spell, Object(unit))
+ else
+ return nil
end
- local GetAuraDataByIndex = C_UnitAuras.GetAuraDataByIndex
- local auraData = GetAuraDataByIndex(ObjectUnit(unit), index, filter)
- if not auraData then return nil end
- return UnpackAuraData(auraData)
- -- return UnitAura(ObjectUnit(unit), index, filter)
- end
- b.UnitBuff = function(unit, index, filter)
- return UnitBuff(ObjectUnit(unit), index, filter)
- end
- b.UnitCanAttack = function(unit1, unit2)
- return UnitCanAttack(ObjectUnit(unit1), ObjectUnit(unit2))
- end
- b.UnitCastingInfo = function(...)
- return UnitCastingInfo(ObjectUnit(...))
- end
- b.UnitChannelInfo = function(...)
- return UnitChannelInfo(ObjectUnit(...))
- end
- b.UnitClass = function(...)
- return UnitClass(ObjectUnit(...))
- end
- b.UnitClassification = function(...)
- return UnitClassification(ObjectUnit(...))
- end
- b.UnitCreatureFamily = function(...)
- return UnitCreatureFamily(ObjectUnit(...))
- end
- b.UnitCreatureType = function(...)
- return UnitCreatureType(ObjectUnit(...))
- end
- b.UnitDebuff = function(unit, index, filter)
- return UnitDebuff(ObjectUnit(unit), index, filter)
- end
- b.UnitExists = function(...)
- return UnitExists(ObjectUnit(...))
- end
- b.UnitGetIncomingHeals = function(unit1, unit2)
- return UnitGetIncomingHeals(ObjectUnit(unit1), ObjectUnit(unit2))
- end
- b.UnitGUID = function(...)
- return ObjectGUID(...) --UnitGUID(ObjectUnit(...))
- end
- b.UnitHealth = function(...)
- return UnitHealth(ObjectUnit(...))
- end
- b.UnitHealthMax = function(...)
- return UnitHealthMax(ObjectUnit(...))
- end
- b.UnitLevel = function(...)
- return UnitLevel(ObjectUnit(...))
end
+ -- b.GetRaidTargetIndex = function(...)
+ -- return GetRaidTargetIndex(Object(...))
+ -- end
+ -- b.GetUnitSpeed = function(...)
+ -- return GetUnitSpeed(Object(...))
+ -- end
+ -- b.C_Spell.IsSpellInRange = function(spell, unit)
+ -- if Object(unit) then
+ -- return C_Spell.IsSpellInRange(spell, Object(unit))
+ -- else
+ -- return false
+ -- end
+ -- end
+ -- b.UnitAffectingCombat = function(...)
+ -- return UnitAffectingCombat(Object(...))
+ -- end
+ -- b.UnitAttackSpeed = function(...)
+ -- return UnitAttackSpeed(Object(...))
+ -- end
+ -- -- b.UnitAura = function(unit, index, filter)
+ -- -- local unpack = unpack
+ -- -- local function UnpackAuraData(auraData)
+ -- -- if not auraData then
+ -- -- return nil
+ -- -- end
+ -- -- return auraData.name,
+ -- -- auraData.icon,
+ -- -- auraData.applications,
+ -- -- auraData.dispelName,
+ -- -- auraData.duration,
+ -- -- auraData.expirationTime,
+ -- -- auraData.sourceUnit,
+ -- -- auraData.isStealable,
+ -- -- auraData.nameplateShowPersonal,
+ -- -- auraData.spellId,
+ -- -- auraData.canApplyAura,
+ -- -- auraData.isBossAura,
+ -- -- auraData.isFromPlayerOrPlayerPet,
+ -- -- auraData.nameplateShowAll,
+ -- -- auraData.timeMod,
+ -- -- unpack(auraData.points)
+ -- -- end
+ -- -- local GetAuraDataByIndex = C_UnitAuras.GetAuraDataByIndex
+ -- -- local auraData = GetAuraDataByIndex(Object(unit), index, filter)
+ -- -- if not auraData then return nil end
+ -- -- return UnpackAuraData(auraData)
+ -- -- -- return UnitAura(Object(unit), index, filter)
+ -- -- end
+ -- b.UnitBuff = function(unit, index, filter)
+ -- return UnitBuff(Object(unit), index, filter)
+ -- end
+ -- b.UnitCanAttack = function(unit1, unit2)
+ -- return UnitCanAttack(Object(unit1), Object(unit2))
+ -- end
+ -- b.UnitCastingInfo = function(...)
+ -- return UnitCastingInfo(Object(...))
+ -- end
+ -- b.UnitChannelInfo = function(...)
+ -- return UnitChannelInfo(Object(...))
+ -- end
+ -- b.UnitClass = function(...)
+ -- return UnitClass(Object(...))
+ -- end
+ -- b.UnitClassification = function(...)
+ -- return UnitClassification(Object(...))
+ -- end
+ -- b.UnitCreatureFamily = function(...)
+ -- return UnitCreatureFamily(Object(...))
+ -- end
+ -- b.UnitCreatureType = function(...)
+ -- return UnitCreatureType(Object(...))
+ -- end
+ -- b.UnitDebuff = function(unit, index, filter)
+ -- return UnitDebuff(Object(unit), index, filter)
+ -- end
+ -- b.UnitExists = function(...)
+ -- return UnitExists(Object(...))
+ -- end
+ -- b.UnitGetIncomingHeals = function(unit1, unit2)
+ -- return UnitGetIncomingHeals(Object(unit1), Object(unit2))
+ -- end
+ -- b.UnitGUID = function(...)
+ -- return ObjectGUID(...) --UnitGUID(Object(...))
+ -- end
+ -- b.UnitHealth = function(...)
+ -- return UnitHealth(Object(...))
+ -- end
+ -- b.UnitHealthMax = function(...)
+ -- return UnitHealthMax(Object(...))
+ -- end
+ -- b.UnitLevel = function(...)
+ -- return UnitLevel(Object(...))
+ -- end
b.UnitName = function(...)
- return UnitName(ObjectUnit(...))
- end
- b.UnitInParty = function(...)
- return UnitInParty(ObjectUnit(...))
- end
- b.UnitInRaid = function(...)
- return UnitInRaid(ObjectUnit(...))
- end
- b.UnitInRange = function(...)
- return UnitInRange(ObjectUnit(...))
- end
- b.UnitIsCharmed = function(...)
- return UnitIsCharmed(ObjectUnit(...))
- end
- b.UnitIsConnected = function(...)
- return UnitIsConnected(ObjectUnit(...))
- end
- b.UnitIsDeadOrGhost = function(...)
- return UnitIsDeadOrGhost(ObjectUnit(...))
- end
- b.UnitIsEnemy = function(unit1, unit2)
- return UnitIsEnemy(ObjectUnit(unit1), ObjectUnit(unit2))
- end
- b.UnitIsFriend = function(unit1, unit2)
- return UnitIsFriend(ObjectUnit(unit1), ObjectUnit(unit2))
- end
- b.UnitIsPlayer = function(...)
- return UnitIsPlayer(ObjectUnit(...))
- end
- b.UnitIsUnit = function(unit1, unit2)
- return UnitIsUnit(ObjectUnit(unit1), ObjectUnit(unit2))
- end
- b.UnitIsVisible = function(...)
- return UnitIsVisible(ObjectUnit(...))
- end
- b.UnitOnTaxi = function(...)
- return UnitOnTaxi(ObjectUnit(...))
- end
- b.UnitPhaseReason = function(...)
- return UnitPhaseReason(ObjectUnit(...))
- end
- b.UnitPower = function(unit, powerType)
- return UnitPower(ObjectUnit(unit), powerType)
- end
- b.UnitPowerMax = function(unit, powerType)
- return UnitPowerMax(ObjectUnit(unit), powerType)
- end
- b.UnitRace = function(...)
- return UnitRace(ObjectUnit(...))
- end
- b.UnitReaction = function(unit1, unit2)
- return UnitReaction(ObjectUnit(unit1), ObjectUnit(unit2))
- end
- b.UnitStat = function(unit, statIndex)
- return UnitStat(ObjectUnit(unit), statIndex)
- end
- b.UnitIsTapDenied = function(...)
- return UnitIsTapDenied(ObjectUnit(...))
- end
- b.UnitThreatSituation = function(unit1, unit2)
- return UnitThreatSituation(ObjectUnit(unit1), ObjectUnit(unit2))
- end
- b.UnitIsTrivial = function(...)
- return UnitIsTrivial(ObjectUnit(...))
+ if Object(...) then
+ return UnitName(Object(...))
+ else
+ return ""
+ end
end
+ -- b.UnitInParty = function(...)
+ -- return UnitInParty(Object(...))
+ -- end
+ -- b.UnitInRaid = function(...)
+ -- return UnitInRaid(Object(...))
+ -- end
+ -- b.UnitInRange = function(...)
+ -- return UnitInRange(Object(...))
+ -- end
+ -- b.UnitIsCharmed = function(...)
+ -- return UnitIsCharmed(Object(...))
+ -- end
+ -- b.UnitIsConnected = function(...)
+ -- return UnitIsConnected(Object(...))
+ -- end
+ -- b.UnitIsDeadOrGhost = function(...)
+ -- return UnitIsDeadOrGhost(Object(...))
+ -- end
+ -- b.UnitIsEnemy = function(unit1, unit2)
+ -- return UnitIsEnemy(Object(unit1), Object(unit2))
+ -- end
+ -- b.UnitIsFriend = function(unit1, unit2)
+ -- return UnitIsFriend(Object(unit1), Object(unit2))
+ -- end
+ -- b.UnitIsPlayer = function(...)
+ -- return UnitIsPlayer(Object(...))
+ -- end
+ -- b.UnitIsUnit = function(unit1, unit2)
+ -- return UnitIsUnit(Object(unit1), Object(unit2))
+ -- end
+ -- b.UnitIsVisible = function(...)
+ -- return UnitIsVisible(Object(...))
+ -- end
+ -- b.UnitOnTaxi = function(...)
+ -- return UnitOnTaxi(Object(...))
+ -- end
+ -- b.UnitPhaseReason = function(...)
+ -- return UnitPhaseReason(Object(...))
+ -- end
+ -- b.UnitPower = function(unit, powerType)
+ -- return UnitPower(Object(unit), powerType)
+ -- end
+ -- b.UnitPowerMax = function(unit, powerType)
+ -- return UnitPowerMax(Object(unit), powerType)
+ -- end
+ -- b.UnitRace = function(...)
+ -- return UnitRace(Object(...))
+ -- end
+ -- b.UnitReaction = function(unit1, unit2)
+ -- return UnitReaction(Object(unit1), Object(unit2))
+ -- end
+ -- b.UnitStat = function(unit, statIndex)
+ -- return UnitStat(Object(unit), statIndex)
+ -- end
+ -- b.UnitIsTapDenied = function(...)
+ -- return UnitIsTapDenied(Object(...))
+ -- end
+ -- b.UnitThreatSituation = function(unit1, unit2)
+ -- return UnitThreatSituation(Object(unit1), Object(unit2))
+ -- end
+ -- b.UnitIsTrivial = function(...)
+ -- return UnitIsTrivial(Object(...))
+ -- end
- br.unlocker = "Tinkr"
+ br.unlockers.selected = "Tinkr"
return true
end
diff --git a/Unlockers/ucs.lua b/Unlockers/ucs.lua
deleted file mode 100644
index 04419bbf4d..0000000000
--- a/Unlockers/ucs.lua
+++ /dev/null
@@ -1,74 +0,0 @@
-local _, br = ...
-local b = br._G
-local unlock = br.unlock
--- UCS Unlocker
-function unlock.UCSUnlock()
- local unlocked = false
- -- Active Player
- b.StopFalling = StopFalling
- b.FaceDirection = FaceDirection
- -- Object
- b.ObjectTypes = ObjectTypes
- b.ObjectPointer = ObjectPointer
- b.ObjectExists = ObjectExists
- b.ObjectIsVisible = ObjectIsVisible
- b.ObjectPosition = ObjectPosition
- b.ObjectFacing = ObjectFacing
- b.ObjectName = ObjectName
- b.ObjectID = ObjectID
- b.ObjectIsUnit = ObjectIsUnit
- b.GetAnglesBetweenObjects = GetAnglesBetweenObjects
- b.GetDistanceBetweenPositions = GetDistanceBetweenPositions
- b.GetDistanceBetweenObjects = GetDistanceBetweenObjects
- b.GetPositionBetweenObjects = GetPositionBetweenObjects
- b.GetPositionFromPosition = GetPositionFromPosition
- b.ObjectIsFacing = ObjectIsFacing
- b.ObjectInteract = ObjectInteract
- -- Object Manager
- b.GetObjectCount = GetObjectCount
- b.GetObjectWithIndex = GetObjectWithIndex
- b.GetObjectWithGUID = GetObjectWithGUID
- b.GetNpcCount = GetObjectCount
- b.GetPlayerCount = GetObjectCount
- b.GetNpcWithIndex = GetObjectWithIndex
- b.GetPlayerWithIndex = GetObjectWithIndex
- -- Unit
- b.UnitBoundingRadius = UnitBoundingRadius
- b.UnitCombatReach = UnitCombatReach
- b.UnitTarget = UnitTarget
- b.UnitCastID = UnitCastID
- -- World
- b.TraceLine = TraceLine
- b.GetCameraPosition = GetCameraPosition
- b.CancelPendingSpell = CancelPendingSpell
- b.ClickPosition = ClickPosition
- b.IsAoEPending = IsAoEPending
- b.GetTargetingSpell = GetTargetingSpell
- b.WorldToScreen = WorldToScreen
- b.ScreenToWorld = ScreenToWorld
- b.GetMousePosition = GetMousePosition
- -- Hacks
- b.IsHackEnabled = IsHackEnabled
- b.SetHackEnabled = SetHackEnabled
- -- Files
- b.GetDirectoryFiles = GetDirectoryFiles
- b.ReadFile = ReadFile
- b.WriteFile = WriteFile
- b.CreateDirectory = CreateDirectory
- b.DirectoryExists = DirectoryExists
- b.GetWoWDirectory = GetWoWDirectory
- -- Callbacks
- b.AddEventCallback = AddEventCallback
- -- Misc
- b.SendHTTPRequest = SendHTTPRequest
- b.GetKeyState = GetKeyState
- -- Drawing
- b.GetWoWWindow = GetWoWWindow
- b.Draw2DLine = Draw2DLine
- b.Draw2DText = Draw2DText
- b.WorldToScreenRaw = WorldToScreenRaw
- b.IsQuestObject = IsQuestObject
- br.unlocker = "UCS"
- unlocked = true
- return unlocked
-end
diff --git a/Unlockers/wowadvanced.lua b/Unlockers/wowadvanced.lua
deleted file mode 100644
index 963294120b..0000000000
--- a/Unlockers/wowadvanced.lua
+++ /dev/null
@@ -1,466 +0,0 @@
---------------------------------------------------------------------------------------------------------------------------------
--- unlockList
---------------------------------------------------------------------------------------------------------------------------------
-local unlockList =
-{
- "AcceptBattlefieldPort",
- "AcceptProposal",
- "AcceptTrade",
- "AscendStop",
- "AssistUnit",
- "AttackTarget",
- "CameraOrSelectOrMoveStart",
- "CameraOrSelectOrMoveStop",
- "CancelItemTempEnchantment",
- "CancelLogout",
- "CancelShapeshiftForm",
- "CancelUnitBuff",
- "CanSummonFriend",
- "CastPetAction",
- "CastShapeshiftForm",
- "CastSpell",
- "CastSpellByID",
- "CastSpellByName",
- "ChangeActionBarPage",
- "CheckInteractDistance",
- "ClearOverrideBindings",
- "ClearTarget",
- "CombatTextSetActiveUnit",
- "CopyToClipboard",
- "CreateMacro",
- "DeleteCursorItem",
- "DeleteMacro",
- "DemoteAssistant",
- "DescendStop",
- "DestroyTotem",
- "DropItemOnUnit",
- "FocusUnit",
- "FollowUnit",
- "ForceQuit",
- "GetDefaultLanguage",
- "GetPartyAssignment",
- "GetPlayerInfoByGUID",
- "GetRaidTargetIndex",
- "GetReadyCheckStatus",
- "GetUnitName",
- "GetUnitSpeed",
- "GetUnscaledFrameRect",
- "GuildControlSetRank",
- "GuildControlSetRankFlag",
- "GuildDemote",
- "GuildPromote",
- "GuildUninvite",
- "InitiateTrade",
- "InteractUnit",
- "IsItemInRange",
- "IsSpellInRange",
- "JoinBattlefield",
- "JumpOrAscendStart",
- "Logout",
- "MoveBackwardStart",
- "MoveBackwardStop",
- "MoveForwardStart",
- "MoveForwardStop",
- "PetAssistMode",
- "PetAttack",
- "PetDefensiveAssistMode",
- "PetDefensiveMode",
- "PetFollow",
- "PetPassiveMode",
- "PetStopAttack",
- "PetWait",
- "PickupAction",
- "PickupCompanion",
- "PickupMacro",
- "PickupPetAction",
- "PickupSpell",
- "PickupSpellBookItem",
- "PitchDownStart",
- "PitchDownStop",
- "PitchUpStart",
- "PromoteToAssistant",
- "Quit",
- "ReplaceEnchant",
- "ReplaceTradeEnchant",
- "RunMacro",
- "RunMacroText",
- "SendChatMessage",
- "SetBinding",
- "SetBindingClick",
- "SetBindingItem",
- "SetBindingMacro",
- "SetBindingSpell",
- "SetCurrentTitle",
- "SetMoveEnabled",
- "SetOverrideBinding",
- "SetOverrideBindingClick",
- "SetOverrideBindingItem",
- "SetOverrideBindingMacro",
- "SetOverrideBindingSpell",
- "SetPortraitTexture",
- "SetRaidTarget",
- "SetTurnEnabled",
- "ShowUIPanel",
- "SitStandOrDescendStart",
- "SpellCanTargetUnit",
- "SpellIsTargeting",
- "SpellStopCasting",
- "SpellStopTargeting",
- "SpellTargetItem",
- "SpellTargetUnit",
- "StartAttack",
- "StrafeLeftStart",
- "StrafeLeftStop",
- "StrafeRightStart",
- "StrafeRightStop",
- "Stuck",
- "SummonFriend",
- "SwapRaidSubgroup",
- "TargetLastEnemy",
- "TargetLastTarget",
- "TargetNearestEnemy",
- "TargetNearestFriend",
- "TargetUnit",
- "ToggleAutoRun",
- "ToggleGameMenu",
- "ToggleRun",
- "ToggleSpellAutocast",
- "TurnLeftStart",
- "TurnLeftStop",
- "TurnOrActionStart",
- "TurnOrActionStop",
- "TurnRightStart",
- "TurnRightStop",
- "UninviteUnit",
- "UnitAffectingCombat",
- "UnitArmor",
- "UnitAttackPower",
- "UnitAttackSpeed",
- "UnitAura",
- "UnitAuraSlots",
- "UnitBuff",
- "UnitCanAssist",
- "UnitCanAttack",
- "UnitCanCooperate",
- "UnitCastingInfo",
- "UnitChannelInfo",
- "UnitClass",
- "UnitClassification",
- "UnitCreatureFamily",
- "UnitCreatureType",
- "UnitDamage",
- "UnitDebuff",
- "UnitDetailedThreatSituation",
- "UnitExists",
- "UnitGetIncomingHeals",
- "UnitGetTotalHealAbsorbs",
- "UnitGroupRolesAssigned",
- "UnitGUID",
- "UnitHealth",
- "UnitHealthMax",
- "UnitInBattleground",
- "UnitInParty",
- "UnitInRaid",
- "UnitInRange",
- "UnitIsAFK",
- "UnitIsCharmed",
- "UnitIsConnected",
- "UnitIsCorpse",
- "UnitIsDead",
- "UnitIsDeadOrGhost",
- "UnitIsDND",
- "UnitIsEnemy",
- "UnitIsFeignDeath",
- "UnitIsFriend",
- "UnitIsGhost",
- "UnitIsInMyGuild",
- "UnitIsPlayer",
- "UnitIsPossessed",
- "UnitIsPVP",
- "UnitIsPVPFreeForAll",
- "UnitIsPVPSanctuary",
- "UnitIsSameServer",
- "UnitIsTrivial",
- "UnitIsUnit",
- "UnitIsVisible",
- "UnitLevel",
- "UnitName",
- "UnitOnTaxi",
- "UnitPhaseReason",
- "UnitPlayerControlled",
- "UnitPlayerOrPetInParty",
- "UnitPlayerOrPetInRaid",
- "UnitPower",
- "UnitPower",
- "UnitPowerMax",
- "UnitPowerType",
- "UnitPVPName",
- "UnitRace",
- "UnitRangedAttackPower",
- "UnitRangedDamage",
- "UnitReaction",
- "UnitSelectionColor",
- "UnitSex",
- "UnitStat",
- "UnitThreatSituation",
- "UnitUsingVehicle",
- "UnitXP",
- "UnitXPMax",
- "UseAction",
- "UseContainerItem",
- "UseInventoryItem",
- "UseItemByName",
- "UseToy",
- "UseToyByName"
-}
-
---------------------------------------------------------------------------------------------------------------------------------
--- functions exported to BadRotations
---------------------------------------------------------------------------------------------------------------------------------
-local _, br = ...
-local b = br._G
-local unlock = br.unlock
-local wa = nil
-local funcCopies = {}
-
--- helper function
-local function stringsplit(inputstr, sep)
- if sep == nil then
- sep = "%s"
- end
- local t = {}
- for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do
- table.insert(t, str)
- end
- return t
-end
-
--- obtain references to WA APIs from the BadRotations plugin
-if _G.BR_WA then
- wa = _G.BR_WA
- _G.BR_WA = nil
-
- -- make a backup copy of all APIs before AddOns hook them
- for i = 1, #unlockList do
- local func = unlockList[i]
- funcCopies[func] = _G[func]
- end
-else
- -- either not WA or BR addon is not enabled
-end
-
-
-function unlock.WowAdUnlock()
- if not wa then
- return false
- end
- --------------------------------
- -- API unlocking
- --------------------------------
- for k, v in pairs(funcCopies) do
- b[k] = function(...) return wa.CallSecureFunction(v, ...) end
- end
-
- --------------------------------
- -- API copy/rename/unlock
- --------------------------------
- b.ReadFile = wa.ReadFile
- b.DirectoryExists = wa.DirectoryExists
- b.WriteFile = wa.WriteFile
- b.ClickPosition = wa.ClickPosition
- b.CreateDirectory = wa.CreateDirectory
- b.GetKeyState = wa.GetKeyState
- b.ObjectName = wa.ObjectName
- b.GetObjectWithIndex = wa.GetObjectWithIndex
- b.ObjectPosition = wa.GetUnitPosition
- b.UnitMovementFlags = wa.GetUnitMovementFlags
- b.GetWoWDirectory = wa.GetWowDirectory
- b.ObjectFacing = wa.UnitFacing
- b.GetMousePosition = b.GetCursorPosition
- b.ObjectExists = wa.ObjectExists
- b.GetCameraPosition = wa.GetCameraPosition
- b.CancelPendingSpell = b.SpellStopTargeting
- b.ObjectIsVisible = b.UnitIsVisible
- b.IsAoEPending = b.SpellIsTargeting
- b.ObjectInteract = b.InteractUnit
- b.UnitFacing = wa.UnitFacing
- b.GetObjectCount = wa.GetObjectCount
- b.GetNewObjects = wa.GetNewObjects
-
- --------------------------------
- -- object fields
- --------------------------------
- b.UnitTarget = function(unit)
- return wa.ObjectField(unit, 0x1C58, 15)
- end
- b.UnitCreator = function(unit)
- return wa.ObjectField(unit, 0x13C8, 15)
- end
- b.UnitBoundingRadius = function(unit)
- return wa.ObjectField(unit, 0x1CEC, 10)
- end
- b.UnitCombatReach = function(unit)
- return wa.ObjectField(unit, 0x1CF0, 10)
- end
- --------------------------------
- -- API conversions
- --------------------------------
- b.ObjectPointer = function(...)
- if b.UnitExists(...) then
- return b.UnitGUID(...)
- end
- end
- b.ObjectIsUnit = function(...)
- local ObjType = wa.ObjectType(...)
- return ObjType == 5
- end
- b.ObjectID = function(object)
- local guid = b.UnitGUID(object)
- if guid then
- local _, _, _, _, _, objectId, _ = strsplit("-", guid);
- return tonumber(objectId);
- else
- return 0
- end
- end
- b.TraceLine = function(...)
- local hit, hitx, hity, hitz = wa.TraceLine(...)
- if hit == 1 then
- return hitx, hity, hitz
- else
- return nil
- end
- end
- b.UnitCastID = function(...)
- local spellId1 = select(9, b.UnitCastingInfo(...)) or 0
- local spellId2 = select(9, b.UnitChannelInfo(...)) or 0
- local castGUID = b.UnitTarget(select(1,...))
- return spellId1, spellId2, castGUID, castGUID
- end
- b.GetDirectoryFiles = function(...)
- local filter = stringsplit(..., "*")
- local files = wa.GetDirectoryFiles(filter[1], "*.lua")
- return stringsplit(files, "|")
- end
- b.WorldToScreen = function(...)
- local multiplier = UIParent:GetScale()
- local sX, sY = wa.WorldToScreen(...)
- return sX * multiplier, -sY * multiplier
- end
- b.FaceDirection = function(arg)
- if type(arg) == "number" then
- wa.FaceDirection(arg)
- else
- arg = b.GetAnglesBetweenObjects("player", arg)
- wa.FaceDirection(arg)
- end
- end
- b.GetObjectWithGUID = function(...)
- return ...
- end
- b.IsHackEnabled = function(...) return false end
- --------------------------------
- -- math
- --------------------------------
- b.GetDistanceBetweenPositions = function(X1, Y1, Z1, X2, Y2, Z2)
- return math.sqrt(math.pow(X2 - X1, 2) + math.pow(Y2 - Y1, 2) + math.pow(Z2 - Z1, 2))
- end
- b.GetAnglesBetweenObjects = function(Object1, Object2)
- if Object1 and Object2 then
- local X1, Y1, Z1 = b.ObjectPosition(Object1)
- local X2, Y2, Z2 = b.ObjectPosition(Object2)
- return math.atan2(Y2 - Y1, X2 - X1) % (math.pi * 2),
- math.atan((Z1 - Z2) / math.sqrt(math.pow(X1 - X2, 2) + math.pow(Y1 - Y2, 2))) % math.pi
- else
- return 0, 0
- end
- end
- b.GetAnglesBetweenPositions = function(X1, Y1, Z1, X2, Y2, Z2)
- return math.atan2(Y2 - Y1, X2 - X1) % (math.pi * 2),
- math.atan((Z1 - Z2) / math.sqrt(math.pow(X1 - X2, 2) + math.pow(Y1 - Y2, 2))) % math.pi
- end
- b.GetPositionFromPosition = function(X, Y, Z, Distance, AngleXY, AngleXYZ)
- return math.cos(AngleXY) * Distance + X, math.sin(AngleXY) * Distance + Y, math.sin(AngleXYZ) * Distance + Z
- end
- b.GetPositionBetweenPositions = function(X1, Y1, Z1, X2, Y2, Z2, DistanceFromPosition1)
- local AngleXY, AngleXYZ = b.GetAnglesBetweenPositions(X1, Y1, Z1, X2, Y2, Z2)
- return b.GetPositionFromPosition(X1, Y1, Z1, DistanceFromPosition1, AngleXY, AngleXYZ)
- end
- b.GetPositionBetweenObjects = function(unit1, unit2, DistanceFromPosition1)
- local X1, Y1, Z1 = b.ObjectPosition(unit1)
- local X2, Y2, Z2 = b.ObjectPosition(unit2)
- if not X1 or not X2 then return end
- local AngleXY, AngleXYZ = b.GetAnglesBetweenPositions(X1, Y1, Z1, X2, Y2, Z2)
- return b.GetPositionFromPosition(X1, Y1, Z1, DistanceFromPosition1, AngleXY, AngleXYZ)
- end
- b.GetDistanceBetweenObjects = function(unit1, unit2)
- local X1, Y1, Z1 = b.ObjectPosition(unit1)
- local X2, Y2, Z2 = b.ObjectPosition(unit2)
- return math.sqrt((X2-X1)^2 + (Y2-Y1)^2 + (Z2-Z1)^2)
- end
- b.ObjectIsFacing = function(obj1, obj2, degrees)
- local Facing = b.UnitFacing(obj1)
- local AngleToUnit = b.GetAnglesBetweenObjects(obj1, obj2)
- local AngleDifference = Facing > AngleToUnit and Facing - AngleToUnit or AngleToUnit - Facing
- local ShortestAngle = AngleDifference < math.pi and AngleDifference or math.pi * 2 - AngleDifference
- degrees = degrees and b.rad(degrees) / 2 or math.pi / 2
- return ShortestAngle < degrees
- end
- --------------------------------
- -- extra APIs
- --------------------------------
- b.AuraUtil = {}
- b.AuraUtil.FindAuraByName = function(...)
- return wa.CallSecureFunction(_G.AuraUtil["FindAuraByName"], ...)
- end
- b.ObjectIsGameObject = function(...)
- local ObjType = wa.ObjectType(...)
- return ObjType == 8 or ObjType == 11
- end
- b.GetMapId = function()
- return select(8, GetInstanceInfo())
- end
- --------------------------------
- -- missing APIs
- --------------------------------
- b.IsQuestObject = function(obj)
- return false
- end
- b.ScreenToWorld = function()
- return 0, 0
- end
- --------------------------------
- -- cached functions
- --------------------------------
- b.ObjectExists = wa.createCachedFunction(b.ObjectExists, 1)
- b.UnitExists = wa.createCachedFunction(b.UnitExists, 1)
- b.UnitIsVisible = wa.createCachedFunction(b.UnitIsVisible, 1)
- b.ObjectIsVisible = wa.createCachedFunction(b.ObjectIsVisible, 1)
-
- b.ObjectPointer = wa.createCachedFunction(b.ObjectPointer, 2)
- b.UnitGUID = wa.createCachedFunction(b.UnitGUID, 2)
- b.UnitIsDeadOrGhost = wa.createCachedFunction(b.UnitIsDeadOrGhost, 2)
- b.UnitHealthMax = wa.createCachedFunction(b.UnitHealthMax, 2)
- b.UnitHealth = wa.createCachedFunction(b.UnitHealth, 2)
- b.ObjectIsUnit = wa.createCachedFunction(b.ObjectIsUnit, 2)
- b.ObjectID = wa.createCachedFunction(b.ObjectID, 2)
- b.UnitName = wa.createCachedFunction(b.UnitName, 2)
- b.ObjectName = wa.createCachedFunction(b.ObjectName, 2)
- b.UnitIsPlayer = wa.createCachedFunction(b.UnitIsPlayer, 2)
- b.UnitAffectingCombat = wa.createCachedFunction(b.UnitAffectingCombat, 2)
- b.UnitTarget = wa.createCachedFunction(b.UnitTarget, 2)
- b.UnitCombatReach = wa.createCachedFunction(b.UnitCombatReach, 2, true)
- b.UnitBoundingRadius = wa.createCachedFunction(b.UnitBoundingRadius, 2, true)
- b.UnitPhaseReason = wa.createCachedFunction(b.UnitPhaseReason, 2, true)
- b.UnitCreator = wa.createCachedFunction(b.UnitCreator, 2, true)
-
- b.UnitCastingInfo = wa.createCachedFunction(b.UnitCastingInfo, 3)
- b.UnitChannelInfo = wa.createCachedFunction(b.UnitChannelInfo, 3)
- b.UnitCastID = wa.createCachedFunction(b.UnitCastID, 3)
-
- b.UnitIsUnit = function(unit1, unit2)
- return b.UnitGUID(unit1) == b.UnitGUID(unit2)
- end
- br.unlocker = "WA"
- return true
-end
\ No newline at end of file
diff --git a/bindings.xml b/bindings.xml
index e43110c5b9..d8ac88f136 100644
--- a/bindings.xml
+++ b/bindings.xml
@@ -4,16 +4,16 @@
if keystate == "down" then test.updateButton(); end
- if keystate == "down" then br.customToggle = true; br.toggleKey = "Rotation"; br.UpdateToggle("Rotation",0.25); else br.customToggle = nil; br.toggleKey = nil; end
+ if keystate == "down" then br.customToggle = true; br.toggleKey = "Rotation"; br.ui:UpdateToggle("Rotation",0.25); else br.customToggle = nil; br.toggleKey = nil; end
- if keystate == "down" then br.customToggle = true; br.toggleKey = "Cooldown"; br.UpdateToggle("Cooldown",0.25); else br.customToggle = nil; br.toggleKey = nil; end
+ if keystate == "down" then br.customToggle = true; br.toggleKey = "Cooldown"; br.ui:UpdateToggle("Cooldown",0.25); else br.customToggle = nil; br.toggleKey = nil; end
- if keystate == "down" then br.customToggle = true; br.toggleKey = "Defensive"; br.UpdateToggle("Defensive",0.25); else br.customToggle = nil; br.toggleKey = nil; end
+ if keystate == "down" then br.customToggle = true; br.toggleKey = "Defensive"; br.ui:UpdateToggle("Defensive",0.25); else br.customToggle = nil; br.toggleKey = nil; end
- if keystate == "down" then br.customToggle = true; br.toggleKey = "Interrupt"; br.UpdateToggle("Interrupt",0.25); else br.customToggle = nil; br.toggleKey = nil; end
+ if keystate == "down" then br.customToggle = true; br.toggleKey = "Interrupt"; br.ui:UpdateToggle("Interrupt",0.25); else br.customToggle = nil; br.toggleKey = nil; end
if keystate == "down" then forceHeal = true; else forceHeal = nil; end
diff --git a/exports.lua b/exports.lua
index bd93d3d11f..be209878c6 100644
--- a/exports.lua
+++ b/exports.lua
@@ -1,5 +1,5 @@
local _, br = ...
local exports = {
- updateButton = function() br.mainButton:Click() end;
+ updateButton = function() br.ui.toggles.mainButton:Click() end;
}
_G.test = exports
\ No newline at end of file
diff --git a/unusedFunctions.lua b/unusedFunctions.lua
index 93e3881ea4..798380c156 100644
--- a/unusedFunctions.lua
+++ b/unusedFunctions.lua
@@ -1,18 +1,23 @@
-- Currently not used functions
local _, br = ...
-function br.IGetLocation(Unit)
- return br.GetObjectPosition(Unit)
+br.unused = br.unused or {}
+local unused = br.unused
+
+function br.unused:IGetLocation(Unit)
+ return br.functions.unit:GetObjectPosition(Unit)
end
-- if canPrepare() then
-function br.canPrepare()
+function br.unused:canPrepare()
if
- br.UnitBuffID("player", 104934) or -- Eating (Feast)
- br.UnitBuffID("player", 80169) or -- Eating
- br.UnitBuffID("player", 87959) or -- Drinking
- br.UnitBuffID("player", 11392) or -- 18 sec Invis Pot
- br.UnitBuffID("player", 3680) or -- 15 sec Invis pot
- br.UnitBuffID("player", 5384) or -- Feign Death
+ br.functions.aura:UnitBuffID("player", 104934) or -- Eating (Feast)
+ br.functions.aura:UnitBuffID("player", 80169) or -- Eating
+ br.functions.aura:UnitBuffID("player", 5005) or -- Food
+ br.functions.aura:UnitBuffID("player", 87959) or -- Drinking
+ br.functions.aura:UnitBuffID("player", 430) or -- Drink
+ br.functions.aura:UnitBuffID("player", 11392) or -- 18 sec Invis Pot
+ br.functions.aura:UnitBuffID("player", 3680) or -- 15 sec Invis pot
+ br.functions.aura:UnitBuffID("player", 5384) or -- Feign Death
br._G.IsMounted()
then
return false
@@ -21,11 +26,11 @@ function br.canPrepare()
end
end
-function br.getAccDistance(Unit1, Unit2)
+function br.unused:getAccDistance(Unit1, Unit2)
-- If both units are visible
if
- br.GetObjectExists(Unit1) and br.GetUnitIsVisible(Unit1) == true and
- (Unit2 == nil or (br.GetObjectExists(Unit2) and br.GetUnitIsVisible(Unit2) == true))
+ br.functions.unit:GetObjectExists(Unit1) and br.functions.unit:GetUnitIsVisible(Unit1) == true and
+ (Unit2 == nil or (br.functions.unit:GetObjectExists(Unit2) and br.functions.unit:GetUnitIsVisible(Unit2) == true))
then
-- If Unit2 is nil we compare player to Unit1
if Unit2 == nil then
@@ -33,16 +38,16 @@ function br.getAccDistance(Unit1, Unit2)
Unit1 = "player"
end
-- if unit1 is player, we can use our lib to get precise range
- if Unit1 == "player" and (br.isDummy(Unit2) or br._G.UnitCanAttack(Unit2, "player") == true) then
+ if Unit1 == "player" and (br.functions.unit:isDummy(Unit2) or br._G.UnitCanAttack(Unit2, "player") == true) then
-- return rc:GetRange(Unit2) or 1000
-- -- else, we use FH positions
-- else
- local X1, Y1, Z1 = br.GetObjectPosition(Unit1)
- local X2, Y2, Z2 = br.GetObjectPosition(Unit2)
+ local X1, Y1, Z1 = br.functions.unit:GetObjectPosition(Unit1)
+ local X2, Y2, Z2 = br.functions.unit:GetObjectPosition(Unit2)
return math.sqrt(((X2 - X1) ^ 2) + ((Y2 - Y1) ^ 2) + ((Z2 - Z1) ^ 2)) -
(math.max(
br._G.UnitCombatReach(Unit2) + br._G.UnitCombatReach(Unit1) + 4 / 3 +
- ((br.isMoving(Unit2) and br.isMoving(Unit1)) and 8 / 3 or 0),
+ ((br.functions.misc:isMoving(Unit2) and br.functions.misc:isMoving(Unit1)) and 8 / 3 or 0),
5
)) --(br._G.UnitCombatReach(Unit2)+UnitBoundingRadius(Unit2))
end
@@ -52,13 +57,13 @@ function br.getAccDistance(Unit1, Unit2)
end
-- findTarget(10,true,1) will find closest target in 10 yard front that have more or equal to 1%hp
-function br.findTarget(range, facingCheck, minimumHealth)
- if br.enemy ~= nil then
- for k, _ in pairs(br.enemy) do
- if br.enemy[k].distance <= range then
- if facingCheck == false or br.getFacing("player", br.enemy[k].unit) == true then
- if not minimumHealth or minimumHealth and minimumHealth >= br.enemy[k].hp then
- br._G.TargetUnit(br.enemy[k].unit)
+function br.unused:findTarget(range, facingCheck, minimumHealth)
+ if br.engines.enemiesEngine.enemy ~= nil then
+ for k, _ in pairs(br.engines.enemiesEngine.enemy) do
+ if br.engines.enemiesEngine.enemy[k].distance <= range then
+ if facingCheck == false or br.functions.unit:getFacing("player", br.engines.enemiesEngine.enemy[k].unit) == true then
+ if not minimumHealth or minimumHealth and minimumHealth >= br.engines.enemiesEngine.enemy[k].hp then
+ br._G.TargetUnit(br.engines.enemiesEngine.enemy[k].unit)
end
end
else
@@ -69,7 +74,7 @@ function br.findTarget(range, facingCheck, minimumHealth)
end
-- if getVengeance() >= 50000 then
-function br.getVengeance()
+function br.unused:getVengeance()
local VengeanceID = 0
if select(3, br._G.UnitClass("player")) == 1 then
VengeanceID = 93098 -- Warrior
@@ -82,31 +87,31 @@ function br.getVengeance()
elseif select(3, br._G.UnitClass("player")) == 11 then
VengeanceID = 84840 -- Druid
end
- if br.UnitBuff("player", VengeanceID) then
- return select(14, br._G.UnitAura("player", br._G.GetSpellInfo(VengeanceID)))
+ if br.functions.aura:UnitBuff("player", VengeanceID) then
+ return select(14, br._G.UnitAura("player", br.api.wow.GetSpellInfo(VengeanceID)))
end
return 0
end
-function br.getLoot2()
+function br.unused:getLoot2()
if br.looted == nil then
br.looted = 0
end
if br.lM:emptySlots() then
for i = 1, br._G.GetObjectCount() do
- if br.GetObjectExists(i) and br._G.bit.band(br._G.GetObjectType(i), br._G.ObjectType.Unit) == 8 then
+ if br.functions.unit:GetObjectExists(i) and br._G.bit.band(br._G.GetObjectType(i), br._G.ObjectType.Unit) == 8 then
local thisUnit = br._G.GetObjectIndex(i)
local hasLoot, canLoot = br._G.CanLootUnit(br._G.UnitGUID(thisUnit))
- local inRange = br.getDistance("player", thisUnit) < 2
+ local inRange = br.functions.range:getDistance("player", thisUnit) < 2
if br._G.UnitIsDeadOrGhost(thisUnit) then
if
hasLoot and canLoot and inRange and
- (br.canLootTimer == nil or br.canLootTimer <= br._G.GetTime() - 0.5) --[[br.getOptionValue("Auto Loot"))]]
+ (br.canLootTimer == nil or br.canLootTimer <= br._G.GetTime() - 0.5) --[[br.functions.misc:getOptionValue("Auto Loot"))]]
then
if br._G.GetCVar("autoLootDefault") == "0" then
br._G.SetCVar("autoLootDefault", "1")
br._G.InteractUnit(thisUnit)
- if br.isLooting() then
+ if br.functions.misc:isLooting() then
return true
end
br.canLootTimer = br._G.GetTime()
@@ -115,7 +120,7 @@ function br.getLoot2()
return
else
br._G.InteractUnit(thisUnit)
- if br.isLooting() then
+ if br.functions.misc:isLooting() then
return true
end
br.canLootTimer = br._G.GetTime()
@@ -125,32 +130,32 @@ function br.getLoot2()
end
end
end
- if br.GetUnitExists("target") and br.GetUnitIsDeadOrGhost("target") and br.looted == 1 and not br.isLooting() then
+ if br.functions.unit:GetUnitExists("target") and br.functions.unit:GetUnitIsDeadOrGhost("target") and br.looted == 1 and not br.functions.misc:isLooting() then
br._G.ClearTarget()
br.looted = 0
end
else
- br.ChatOverlay("Bags are full, nothing will be looted!")
+ br.ui.chatOverlay:Show("Bags are full, nothing will be looted!")
end
end
-- Dem Bleeds
-- In a run once environment we shall create the Tooltip that we will be reading
-- all of the spell details from
-br.nGTT = br._G.CreateFrame("GameTooltip", "MyScanningTooltip", nil, "GameTooltipTemplate")
-br.nGTT:SetOwner(br._G.WorldFrame, "ANCHOR_NONE")
-br.nGTT:AddFontStrings(
- br.nGTT:CreateFontString("$parentTextLeft1", nil, "GameTooltipText"),
- br.nGTT:CreateFontString("$parentTextRight1", nil, "GameTooltipText")
+br.unused.nGTT = br._G.CreateFrame("GameTooltip", "MyScanningTooltip", nil, "GameTooltipTemplate")
+br.unused.nGTT:SetOwner(br._G.WorldFrame, "ANCHOR_NONE")
+br.unused.nGTT:AddFontStrings(
+ br.unused.nGTT:CreateFontString("$parentTextLeft1", nil, "GameTooltipText"),
+ br.unused.nGTT:CreateFontString("$parentTextRight1", nil, "GameTooltipText")
)
-function br.nDbDmg(tar, spellID, player)
+function br.unused:nDbDmg(tar, spellID, player)
if br._G.GetCVar("DotDamage") == nil then
br._G.RegisterCVar("DotDamage", 0)
end
- br.nGTT:ClearLines()
+ br.unused.nGTT:ClearLines()
for i = 1, 40 do
- if br._G.UnitDebuff(tar, i, player) == br._G.GetSpellInfo(spellID) then
- br.nGTT:SetUnitDebuff(tar, i, player)
+ if br._G.UnitDebuff(tar, i, player) == br.api.wow.GetSpellInfo(spellID) then
+ br.unused.nGTT:SetUnitDebuff(tar, i, player)
local scanText = _G["MyScanningTooltipTextLeft2"]:GetText()
local DoTDamage = scanText:match("([0-9]+%.?[0-9]*)")
--if not issecure() then Print(issecure()) end -- function is called inside the profile
@@ -161,10 +166,10 @@ function br.nDbDmg(tar, spellID, player)
end
end
--- br.useItem(12345)
-function br.useItem_old(itemID)
+-- br.functions.item:useItem(12345)
+function br.unused:useItem_old(itemID)
if br._G.GetItemCount(itemID) > 0 then
- if select(2, br._G.GetItemCooldown(itemID)) == 0 then
+ if select(2, br._G.C_Container.GetItemCooldown(itemID)) == 0 then
local itemName = br._G.GetItemInfo(itemID)
br._G.RunMacroText("/use " .. itemName)
return true
@@ -174,7 +179,7 @@ function br.useItem_old(itemID)
end
--[[Taunts Table!! load once]]
-br.tauntsTable = {
+br.unused.tauntsTable = {
{ spell = 143436, stacks = 1 },
--Immerseus/71543 143436 - Corrosive Blast == 1x
{ spell = 146124, stacks = 3 },
@@ -202,14 +207,14 @@ br.tauntsTable = {
{ spell = 145183, stacks = 3 } --Garrosh/71865 145183 - Gripping Despair >= 3x
}
--[[Taunt function!! load once]]
-function br.ShouldTaunt()
+function br.unused:ShouldTaunt()
--[[Normal boss1 taunt method]]
- if not br.GetUnitIsUnit("player", "boss1target") then
+ if not br.functions.unit:GetUnitIsUnit("player", "boss1target") then
for i = 1, #br.tauntsTable do
if
- not br.UnitDebuffID("player", br.tauntsTable[i].spell) and
- br.UnitDebuffID("boss1target", br.tauntsTable[i].spell) and
- br.getDebuffStacks("boss1target", br.tauntsTable[i].spell) >= br.tauntsTable[i].stacks
+ not br.functions.aura:UnitDebuffID("player", br.tauntsTable[i].spell) and
+ br.functions.aura:UnitDebuffID("boss1target", br.tauntsTable[i].spell) and
+ br.functions.aura:getDebuffStacks("boss1target", br.tauntsTable[i].spell) >= br.tauntsTable[i].stacks
then
br._G.TargetUnit("boss1")
return true
@@ -217,9 +222,9 @@ function br.ShouldTaunt()
end
end
--[[Swap back to Wavebinder Kardris]]
- if br.getBossID("target") ~= 71858 then
- if br.UnitDebuffID("player", 144215) and br.getDebuffStacks("player", 144215) >= 6 then
- if br.getBossID("boss1") == 71858 then
+ if br.functions.unit:getBossID("target") ~= 71858 then
+ if br.functions.aura:UnitDebuffID("player", 144215) and br.functions.aura:getDebuffStacks("player", 144215) >= 6 then
+ if br.functions.unit:getBossID("boss1") == 71858 then
br._G.TargetUnit("boss1")
return true
else