--- ============================ HEADER ============================
--- ======= LOCALIZE =======
-- Addon
local addonName , HR = ... ;
-- HeroLib
local HL = HeroLib ;
local Cache = HeroCache ;
local Unit = HL.Unit ;
local Player = Unit.Player ;
local Target = Unit.Target ;
local Spell = HL.Spell ;
local Item = HL.Item ;
local GUI = HL.GUI ;
local CreatePanelOption = GUI.CreatePanelOption ;
-- Lua
local mathmax = math.max ;
local mathmin = math.min ;
local pairs = pairs ;
local select = select ;
-- File Locals
local Masque ; -- , MasqueGroupLeft;
local MasqueGroups = { } ;
local UIFrames ;
local MasqueFrameList ;
local PrevResult , CurrResult ;
local function ModMasque ( Frame , Disabled )
if Disabled then
Frame.__MSQ_Normal : Hide ( ) ;
Frame.Texture : SetAllPoints ( Frame ) ;
if Frame.CooldownFrame then
Frame.CooldownFrame : SetAllPoints ( Frame ) ;
end
if Frame.Backdrop then
Frame.Backdrop : Show ( ) ;
Frame.Backdrop : SetFrameLevel ( mathmin ( Frame.Backdrop : GetFrameLevel ( ) , 7 ) ) ;
end
else
if Frame.Backdrop then
Frame.Backdrop : Hide ( ) ;
end
end
end
local function MasqueUpdate ( Addon , Group , SkinID , Backdrop , Shadow , Gloss , Colors , Disabled )
if Addon == HR and MasqueGroups and MasqueFrameList then
local k = MasqueFrameList [ Group ] ;
if k then
if type ( k.Icon ) == " table " then
for _ , tblIcon in pairs ( k.Icon ) do
ModMasque ( tblIcon , Disabled )
end
else
ModMasque ( k , Disabled )
end
end
end
end
--- ============================ CONTENT ============================
--- ======= BINDINGS =======
BINDING_HEADER_HEROROTATION = " HeroRotation " ;
BINDING_NAME_HEROROTATION_CDS = " Toggle CDs " ;
BINDING_NAME_HEROROTATION_AOE = " Toggle AoE " ;
BINDING_NAME_HEROROTATION_TOGGLE = " Toggle On/Off " ;
BINDING_NAME_HEROROTATION_UNLOCK = " Unlock the addon to move icons " ;
BINDING_NAME_HEROROTATION_LOCK = " Lock the addon in place " ;
--- ======= MAIN FRAME =======
HR.MainFrame = CreateFrame ( " Frame " , " HeroRotation_MainFrame " , UIParent ) ;
HR.MainFrame : SetFrameStrata ( HR.GUISettings . General.MainFrameStrata ) ;
HR.MainFrame : SetFrameLevel ( 10 ) ;
HR.MainFrame : SetWidth ( 112 ) ;
HR.MainFrame : SetHeight ( 96 ) ;
HR.MainFrame : SetClampedToScreen ( true ) ;
-- Resize
function HR . MainFrame : ResizeUI ( Multiplier )
local FramesToResize = {
-- TODO: Put the Size in one Array in UI.lua and pull it out here
{ HR.MainFrame , 112 , 96 } ,
{ HR.MainIconFrame , 64 , 64 } ,
{ HR.SmallIconFrame , 64 , 32 } ,
{ HR.SmallIconFrame . Icon [ 1 ] , HR.GUISettings . General.BlackBorderIcon and 30 or 32 , HR.GUISettings . General.BlackBorderIcon and 30 or 32 } ,
{ HR.SmallIconFrame . Icon [ 2 ] , HR.GUISettings . General.BlackBorderIcon and 30 or 32 , HR.GUISettings . General.BlackBorderIcon and 30 or 32 } ,
{ HR.LeftIconFrame , 48 , 48 } ,
{ HR.SuggestedIconFrame , 32 , 32 } ,
{ HR.RightSuggestedIconFrame , 32 , 32 } ,
{ HR.MainIconPartOverlayFrame , 64 , 64 } ,
} ;
for _ , Value in pairs ( FramesToResize ) do
Value [ 1 ] : SetWidth ( Value [ 2 ] * Multiplier ) ;
Value [ 1 ] : SetHeight ( Value [ 3 ] * Multiplier ) ;
end
for i = 1 , HR.MaxQueuedCasts do
HR.MainIconFrame . Part [ i ] : SetWidth ( 64 * Multiplier ) ;
HR.MainIconFrame . Part [ i ] : SetHeight ( 64 * Multiplier ) ;
end
HR.SuggestedIconFrame : SetPoint ( " BOTTOM " , HR.MainIconFrame , " LEFT " , - HR.LeftIconFrame : GetWidth ( ) / 2 , HR.LeftIconFrame : GetHeight ( ) / 2 + ( HR.GUISettings . General.BlackBorderIcon and 3 * Multiplier or 4 * Multiplier ) ) ;
HR.RightSuggestedIconFrame : SetPoint ( " BOTTOM " , HR.MainIconFrame , " RIGHT " , HR.LeftIconFrame : GetWidth ( ) / 2 , HR.LeftIconFrame : GetHeight ( ) / 2 + ( HR.GUISettings . General.BlackBorderIcon and 3 * Multiplier or 4 * Multiplier ) ) ; -- todo matt fix this location
HeroRotationDB.GUISettings [ " Scaling.ScaleUI " ] = Multiplier ;
end
function HR . MainFrame : ResizeButtons ( Multiplier )
local FramesToResize = {
-- TODO: Put the Size in one Array in UI.lua and pull it out here
{ HR.ToggleIconFrame , 64 , 20 } ,
{ HR.ToggleIconFrame . Button [ 1 ] , 20 , 20 } ,
{ HR.ToggleIconFrame . Button [ 2 ] , 20 , 20 } ,
{ HR.ToggleIconFrame . Button [ 3 ] , 20 , 20 }
} ;
for Key , Value in pairs ( FramesToResize ) do
Value [ 1 ] : SetWidth ( Value [ 2 ] * Multiplier ) ;
Value [ 1 ] : SetHeight ( Value [ 3 ] * Multiplier ) ;
end
for i = 1 , 3 do
HR.ToggleIconFrame . Button [ i ] : SetPoint ( " LEFT " , HR.ToggleIconFrame , " LEFT " , HR.ToggleIconFrame . Button [ i ] : GetWidth ( ) * ( i - 1 ) + i , 0 ) ;
end
HeroRotationDB.GUISettings [ " Scaling.ScaleButtons " ] = Multiplier ;
end
-- Lock/Unlock
local LockSpell = Spell ( 18282 ) ;
function HR . MainFrame : Unlock ( )
-- Grey Texture
HR.ResetIcons ( ) ;
HR.Cast ( LockSpell ) ; -- Main Icon
HR.Cast ( LockSpell , { true } ) ; -- Small Icon 1
HR.Cast ( LockSpell , { true } ) ; -- Small Icon 2
HR.CastLeft ( LockSpell ) ; -- Left Icon
HR.CastSuggested ( LockSpell ) ; -- Suggested Icon
HR.CastRightSuggested ( LockSpell ) ; -- Right Suggested Icon
-- Unlock the UI
for Key , Value in pairs ( UIFrames ) do
Value : EnableMouse ( true ) ;
end
HR.MainFrame : SetMovable ( true ) ;
HR.ToggleIconFrame : SetMovable ( true ) ;
HeroRotationDB.Locked = false ;
end
function HR . MainFrame : Lock ( )
for Key , Value in pairs ( UIFrames ) do
Value : EnableMouse ( false ) ;
end
HR.MainFrame : SetMovable ( false ) ;
HR.ToggleIconFrame : SetMovable ( false ) ;
HeroRotationDB.Locked = true ;
end
function HR . MainFrame : ToggleLock ( )
if HeroRotationDB.Locked then
HR.MainFrame : Unlock ( ) ;
HR.Print ( " HeroRotation UI is now |cff00ff00unlocked|r. " ) ;
else
HR.MainFrame : Lock ( ) ;
HR.Print ( " HeroRotation UI is now |cffff0000locked|r. " ) ;
end
end
-- Start Move
local function StartMove ( self )
self : StartMoving ( ) ;
end
HR.MainFrame : SetScript ( " OnMouseDown " , StartMove ) ;
-- Stop Move
local function StopMove ( self )
self : StopMovingOrSizing ( ) ;
if not HeroRotationDB then HeroRotationDB = { } ; end
local point , relativeTo , relativePoint , xOffset , yOffset , relativeToName ;
point , relativeTo , relativePoint , xOffset , yOffset = self : GetPoint ( ) ;
if not relativeTo then
relativeToName = " UIParent " ;
else
relativeToName = relativeTo : GetName ( ) ;
end
HeroRotationDB.IconFramePos = {
point ,
relativeToName ,
relativePoint ,
xOffset ,
yOffset
} ;
end
HR.MainFrame : SetScript ( " OnMouseUp " , StopMove ) ;
HR.MainFrame : SetScript ( " OnHide " , StopMove ) ;
-- AddonLoaded
HR.MainFrame : RegisterEvent ( " ADDON_LOADED " ) ;
HR.MainFrame : SetScript ( " OnEvent " , function ( self , Event , Arg1 )
if Event == " ADDON_LOADED " then
if Arg1 == " HeroRotation " then
MasqueFrameList = {
[ " Main Icon " ] = HR.MainIconFrame ,
[ " Top Icons " ] = HR.SmallIconFrame ,
[ " Left Icon " ] = HR.LeftIconFrame ,
[ " Suggested Icon " ] = HR.SuggestedIconFrame ,
[ " Right Suggested Icon " ] = HR.RightSuggestedIconFrame ,
[ " Part Overlay " ] = HR.MainIconPartOverlayFrame ,
} ;
if not Masque then
Masque = LibStub ( " Masque " , true )
if Masque then
Masque : Register ( " HeroRotation " , MasqueUpdate , HR )
for FrameName , Frame in pairs ( MasqueFrameList ) do
MasqueGroups [ Frame ] = Masque : Group ( addonName , FrameName )
end
end
end
-- Panels
if type ( HeroRotationDB ) ~= " table " then
HeroRotationDB = { } ;
end
if type ( HeroRotationCharDB ) ~= " table " then
HeroRotationCharDB = { } ;
end
if type ( HeroRotationDB.GUISettings ) ~= " table " then
HeroRotationDB.GUISettings = { } ;
end
if type ( HeroRotationCharDB.GUISettings ) ~= " table " then
HeroRotationCharDB.GUISettings = { } ;
end
HR.GUI . LoadSettingsRecursively ( HR.GUISettings ) ;
HR.GUI . CorePanelSettingsInit ( ) ;
-- UI
if HeroRotationDB and type ( HeroRotationDB.IconFramePos ) == " table " and # HeroRotationDB.IconFramePos == 5 then
HR.MainFrame : SetPoint ( HeroRotationDB.IconFramePos [ 1 ] , _G [ HeroRotationDB.IconFramePos [ 2 ] ] , HeroRotationDB.IconFramePos [ 3 ] , HeroRotationDB.IconFramePos [ 4 ] , HeroRotationDB.IconFramePos [ 5 ] ) ;
else
HR.MainFrame : SetPoint ( " CENTER " , UIParent , " CENTER " , - 200 , 0 ) ;
end
HR.MainFrame : SetFrameStrata ( HR.GUISettings . General.MainFrameStrata ) ;
HR.MainFrame : Show ( ) ;
HR.MainIconFrame : Init ( ) ;
HR.SmallIconFrame : Init ( ) ;
HR.LeftIconFrame : Init ( ) ;
HR.SuggestedIconFrame : Init ( ) ;
HR.RightSuggestedIconFrame : Init ( ) ;
HR.ToggleIconFrame : Init ( ) ;
if HeroRotationDB.GUISettings [ " Scaling.ScaleUI " ] then
HR.MainFrame : ResizeUI ( HeroRotationDB.GUISettings [ " Scaling.ScaleUI " ] ) ;
end
if HeroRotationDB.GUISettings [ " Scaling.ScaleButtons " ] then
HR.MainFrame : ResizeButtons ( HeroRotationDB.GUISettings [ " Scaling.ScaleButtons " ] ) ;
end
for k , v in pairs ( MasqueFrameList ) do
if type ( v.Icon ) == " table " then
for _ , tblIcon in pairs ( v.Icon ) do
tblIcon.GetNormalTexture = function ( self ) return nil end ;
tblIcon.SetNormalTexture = function ( self , Texture ) self.Texture = Texture end ;
end
else
v.GetNormalTexture = function ( self ) return nil end ;
v.SetNormalTexture = function ( self , Texture ) self.Texture = Texture end ;
end
end
if MasqueGroups then
for k , v in pairs ( MasqueGroups ) do
if type ( k.Icon ) == " table " then
for _ , tblIcon in pairs ( k.Icon ) do
if v then v : AddButton ( tblIcon , { Icon = tblIcon.Texture , Cooldown = ( tblIcon.CooldownFrame or nil ) } ) end ;
end
else
if v then v : AddButton ( k , { Icon = k.Texture , Cooldown = k.CooldownFrame } ) end ;
end
end
for k , v in pairs ( MasqueGroups ) do
if v then v : ReSkin ( ) end
end
end
UIFrames = {
HR.MainFrame ,
HR.MainIconFrame ,
HR.MainIconPartOverlayFrame ,
HR.MainIconFrame . Part [ 1 ] ,
HR.MainIconFrame . Part [ 2 ] ,
HR.MainIconFrame . Part [ 3 ] ,
HR.SmallIconFrame ,
HR.SmallIconFrame . Icon [ 1 ] ,
HR.SmallIconFrame . Icon [ 2 ] ,
HR.LeftIconFrame ,
HR.SuggestedIconFrame ,
HR.RightSuggestedIconFrame ,
HR.ToggleIconFrame
} ;
-- Load additionnal settings
local CP_General = GUI.GetPanelByName ( " General " )
if CP_General then
CreatePanelOption ( " Slider " , CP_General , " General.SetAlpha " , { 0 , 1 , 0.05 } , " Addon Alpha " , " Change the addon's alpha setting. " ) ;
CreatePanelOption ( " Button " , CP_General , " ButtonMove " , " Lock/Unlock " , " Enable the moving of the frames. " , function ( ) HR.MainFrame : ToggleLock ( ) ; end ) ;
CreatePanelOption ( " Button " , CP_General , " ButtonReset " , " Reset Buttons " , " Resets the anchor of buttons. " , function ( ) HR.ToggleIconFrame : ResetAnchor ( ) ; end ) ;
end
local CP_Scaling = GUI.GetPanelByName ( " Scaling " )
if CP_Scaling then
CreatePanelOption ( " Slider " , CP_Scaling , " Scaling.ScaleUI " , { 0.5 , 5 , 0.1 } , " UI Scale " , " Scale of the Icons. " , function ( value ) HR.MainFrame : ResizeUI ( value ) ; end ) ;
CreatePanelOption ( " Slider " , CP_Scaling , " Scaling.ScaleButtons " , { 0.5 , 5 , 0.1 } , " Buttons Scale " , " Scale of the Buttons. " , function ( value ) HR.MainFrame : ResizeButtons ( value ) ; end ) ;
CreatePanelOption ( " Slider " , CP_Scaling , " Scaling.ScaleHotkey " , { 0.5 , 5 , 0.1 } , " Hotkey Scale " , " Scale of the Hotkeys. " ) ;
end
-- Modules
C_Timer.After ( 2 , function ( )
HR.MainFrame : UnregisterEvent ( " ADDON_LOADED " ) ;
HR.PulsePreInit ( ) ;
HR.PulseInit ( ) ;
end
) ;
end
end
end
) ;
--- ======= MAIN =======
function HR . PulsePreInit ( )
HR.MainFrame : Lock ( ) ;
end
local EnabledRotation = {
-- Death Knight
[ 250 ] = " HeroRotation_DeathKnight " , -- Blood
[ 251 ] = " HeroRotation_DeathKnight " , -- Frost
[ 252 ] = " HeroRotation_DeathKnight " , -- Unholy
-- Demon Hunter
[ 577 ] = " HeroRotation_DemonHunter " , -- Havoc
[ 581 ] = " HeroRotation_DemonHunter " , -- Vengeance
-- Druid
[ 102 ] = " HeroRotation_Druid " , -- Balance
[ 103 ] = " HeroRotation_Druid " , -- Feral
[ 104 ] = " HeroRotation_Druid " , -- Guardian
--[105] = "HeroRotation_Druid", -- Restoration
-- Evoker
[ 1467 ] = " HeroRotation_Evoker " , -- Devastation
--[1468] = "HeroRotation_Evoker", -- Preservation
[ 1473 ] = " HeroRotation_Evoker " , -- Augmentation
-- Hunter
[ 253 ] = " HeroRotation_Hunter " , -- Beast Mastery
[ 254 ] = " HeroRotation_Hunter " , -- Marksmanship
[ 255 ] = " HeroRotation_Hunter " , -- Survival
-- Mage
[ 62 ] = " HeroRotation_Mage " , -- Arcane
[ 63 ] = " HeroRotation_Mage " , -- Fire
[ 64 ] = " HeroRotation_Mage " , -- Frost
-- Monk
[ 268 ] = " HeroRotation_Monk " , -- Brewmaster
[ 269 ] = " HeroRotation_Monk " , -- Windwalker
--[270] = "HeroRotation_Monk", -- Mistweaver
-- Paladin
--[65] = "HeroRotation_Paladin", -- Holy
[ 66 ] = " HeroRotation_Paladin " , -- Protection
[ 70 ] = " HeroRotation_Paladin " , -- Retribution
-- Priest
--[256] = "HeroRotation_Priest", -- Discipline
--[257] = "HeroRotation_Priest", -- Holy
[ 258 ] = " HeroRotation_Priest " , -- Shadow
-- Rogue
[ 259 ] = " HeroRotation_Rogue " , -- Assassination
[ 260 ] = " HeroRotation_Rogue " , -- Outlaw
[ 261 ] = " HeroRotation_Rogue " , -- Subtlety
-- Shaman
[ 262 ] = " HeroRotation_Shaman " , -- Elemental
[ 263 ] = " HeroRotation_Shaman " , -- Enhancement
--[264] = "HeroRotation_Shaman", -- Restoration
-- Warlock
[ 265 ] = " HeroRotation_Warlock " , -- Affliction
[ 266 ] = " HeroRotation_Warlock " , -- Demonology
[ 267 ] = " HeroRotation_Warlock " , -- Destruction
-- Warrior
[ 71 ] = " HeroRotation_Warrior " , -- Arms
[ 72 ] = " HeroRotation_Warrior " , -- Fury
[ 73 ] = " HeroRotation_Warrior " -- Protection
} ;
local LatestSpecIDChecked = 0 ;
function HR . PulseInit ( )
local Spec = GetSpecialization ( ) ;
-- Delay by 1 second until the WoW API returns a valid value.
if Spec == nil then
HL.PulseInitialized = false ;
C_Timer.After ( 1 , function ( )
HR.PulseInit ( ) ;
end
) ;
else
-- Force a refresh from the Core
-- TODO: Make it a function instead of copy/paste from Core Events.lua
Cache.Persistent . Player.Spec = { GetSpecializationInfo ( Spec ) } ;
local SpecID = Cache.Persistent . Player.Spec [ 1 ] ;
-- Delay by 1 second until the WoW API returns a valid value.
if SpecID == nil then
HL.PulseInitialized = false ;
C_Timer.After ( 1 , function ( )
HR.PulseInit ( ) ;
end
) ;
else
-- Load the Class Module if it's possible and not already loaded
if EnabledRotation [ SpecID ] and not IsAddOnLoaded ( EnabledRotation [ SpecID ] ) then
LoadAddOn ( EnabledRotation [ SpecID ] ) ;
HL.LoadOverrides ( SpecID )
end
-- Check if there is a Rotation for this Spec
if LatestSpecIDChecked ~= SpecID then
if EnabledRotation [ SpecID ] and HR.APLs [ SpecID ] then
for Key , Value in pairs ( UIFrames ) do
Value : Show ( ) ;
end
HR.MainFrame : SetScript ( " OnUpdate " , HR.Pulse ) ;
-- Spec Registers
-- Spells
Player : RegisterListenedSpells ( SpecID ) ;
HL.UnregisterAuraTracking ( ) ;
-- Enums Filters
Player : FilterTriggerGCD ( SpecID ) ;
Spell : FilterProjectileSpeed ( SpecID ) ;
-- Module Init Function
if HR.APLInits [ SpecID ] then
HR.APLInits [ SpecID ] ( ) ;
end
-- Special Checks
if GetCVar ( " nameplateShowEnemies " ) ~= " 1 " then
HR.Print ( " It looks like enemy nameplates are disabled. This may cause the addon to operate incorrectly, leading to incorrect or missing spell suggestions. " ) ;
end
else
HR.Print ( " No Rotation found for this class/spec (SpecID: " .. SpecID .. " ), addon disabled. This is likely due to the rotation being unsupported at this time. Please check supported rotations here: https://github.com/herotc/hero-rotation#supported-rotations " ) ;
for Key , Value in pairs ( UIFrames ) do
Value : Hide ( ) ;
end
HR.MainFrame : SetScript ( " OnUpdate " , nil ) ;
end
LatestSpecIDChecked = SpecID ;
end
if not HL.PulseInitialized then HL.PulseInitialized = true ; end
end
end
end
HR.Timer = {
Pulse = 0
} ;
function HR . Pulse ( )
if GetTime ( ) > HR.Timer . Pulse and HR.Locked ( ) then
HR.Timer . Pulse = GetTime ( ) + HL.Timer . PulseOffset ;
HR.ResetIcons ( ) ;
-- Check if the current spec is available (might not always be the case)
-- Especially when switching from area (open world -> instance)
local SpecID = Cache.Persistent . Player.Spec [ 1 ] ;
if SpecID then
-- Check if we are ready to cast something to save FPS.
if HR.ON ( ) and HR.Ready ( ) then
HL.CacheHasBeenReset = false ;
Cache.Reset ( ) ;
-- Rotational Debug Output
if HR.DebugON ( ) then
CurrResult = HR.APLs [ SpecID ] ( ) ;
if CurrResult and CurrResult ~= PrevResult then
HR.Print ( CurrResult ) ;
PrevResult = CurrResult ;
end
else
HR.APLs [ SpecID ] ( ) ;
end
end
if MasqueGroups then
for k , v in pairs ( MasqueGroups ) do
if v then v : ReSkin ( ) end
end
end
end
end
end
-- Is the player ready ?
function HR . Ready ( )
local AreWeReady
if HR.GUISettings . General.ShowWhileMounted then
AreWeReady = not Player : IsDeadOrGhost ( ) and not Player : IsInVehicle ( ) and not C_PetBattles.IsInBattle ( ) ;
else
AreWeReady = not Player : IsDeadOrGhost ( ) and not Player : IsMounted ( ) and not Player : IsInVehicle ( ) and not C_PetBattles.IsInBattle ( ) ;
end
return AreWeReady
end
-- Used to force a short/long pulse wait, it also resets the icons.
function HR . ChangePulseTimer ( Offset )
HR.ResetIcons ( ) ;
HR.Timer . Pulse = GetTime ( ) + Offset ;
end