local configCache , _ , T = { } , ...
local PC , api , iapi , MODERN = T.OPieCore , { } , { } , select ( 4 , GetBuildInfo ( ) ) >= 10e4
local GameTooltip = T.NotGameTooltip or GameTooltip
local max , min , abs , floor , sin , cos = math.max , math.min , math.abs , math.floor , sin , cos
local MIN_ANIMATION_FPS , LOCKED_FRAMERATE = 20 , 60 do
local ticks = 0
local function unlockTick ( )
if ticks < 2 then
ticks = ticks + 1
C_Timer.After ( 0.55 , unlockTick )
else
LOCKED_FRAMERATE = nil
end
end
C_Timer.After ( 0 , unlockTick )
end
local Slices , GhostIndication , IndicatorFactories = { } , { } , { }
local CreateIndicator , ActiveIndicatorFactory , LastRegisteredIndicatorFactory = T.Mirage . CreateIndicator
local function assert ( condition , text , level )
return condition or error ( text , level or 2 ) ( ( 0 ) [ 0 ] )
end
local gfxBase = ( [[Interface\AddOns\%s\gfx\]] ) : format ( ( ... ) )
local anchorFrame = CreateFrame ( " Frame " )
anchorFrame : SetSize ( 1 , 1 )
anchorFrame : SetPoint ( " CENTER " )
anchorFrame : Hide ( )
local mainFrame = CreateFrame ( " Frame " , nil , UIParent )
mainFrame : Hide ( )
mainFrame : SetSize ( 128 , 128 )
mainFrame : SetPoint ( " CENTER " , anchorFrame )
mainFrame : SetFrameStrata ( " FULLSCREEN " )
local centerPointer = mainFrame : CreateTexture ( nil , " ARTWORK " )
centerPointer : SetSize ( 192 , 192 )
centerPointer : SetPoint ( " CENTER " )
centerPointer : SetTexture ( gfxBase .. " pointer " )
local ringQuad , setRingRotationPeriod , centerCircle , centerGlow = { } do
local quadPoints , animations = { " BOTTOMRIGHT " , " BOTTOMLEFT " , " TOPLEFT " , " TOPRIGHT " } , { }
for i = 1 , 4 do
local qf = CreateFrame ( " Frame " , nil , mainFrame )
qf : SetSize ( 32 , 32 )
qf : SetPoint ( quadPoints [ i ] , mainFrame , " CENTER " )
ringQuad [ i ] = qf
end
centerCircle = T.CreateQuadTexture ( " ARTWORK " , 64 , gfxBase .. " circle " , nil , ringQuad )
centerGlow = T.CreateQuadTexture ( " BACKGROUND " , 128 , gfxBase .. " glow " , nil , ringQuad )
for i = 1 , 4 do
local g , a = ringQuad [ i ] : CreateAnimationGroup ( )
g : SetLooping ( " REPEAT " )
a = g : CreateAnimation ( " Rotation " )
a : SetOrigin ( quadPoints [ i ] , 0 , 0 )
a : SetDuration ( 4 )
a : SetDegrees ( - 360 )
animations [ i ] = a
g : Play ( )
end
function setRingRotationPeriod ( p )
local p = max ( 0.1 , p )
for i = 1 , 4 do animations [ i ] : SetDuration ( p ) end
end
end
local function SetAngle ( self , angle , radius )
self : SetPoint ( " CENTER " , radius * cos ( 90 + angle ) , radius * cos ( angle ) )
end
local function CalculateRingRadius ( n , fLength , aLength , min , baseAngle )
if n < 2 then return min end
local radius , mLength , astep = max ( min , ( fLength + aLength * ( n - 1 ) ) / 6.2831853071796 ) , ( fLength + aLength ) / 2 , 360 / n
repeat
local ox , oy , clear , angle , i = radius * cos ( baseAngle ) , radius * sin ( baseAngle ) , true , baseAngle + astep , 1
while clear and i <= n do
local nx , ny , sideLength = radius * cos ( angle ) , radius * sin ( angle ) , ( i == 1 or i == n ) and mLength or aLength
if abs ( ox - nx ) < sideLength and abs ( oy - ny ) < sideLength then
radius , clear = radius + 5
end
ox , oy , angle , i = nx , ny , angle + astep , i + 1
end
until clear
return radius
end
local function SetupTransitionAnimation ( anim , uf )
local fps , _ , screenHeight = LOCKED_FRAMERATE or GetFramerate ( ) , GetPhysicalScreenSize ( )
local radiusRaw , hadOnUpdate = mainFrame.radius , mainFrame : IsVisible ( ) and mainFrame : GetScript ( " OnUpdate " )
local radiusPix = ( 34 + radiusRaw ) * UIParent : GetEffectiveScale ( ) * configCache.RingScale / 768 * screenHeight
local zoomTime = configCache.XTAnimation and fps >= MIN_ANIMATION_FPS and 0.25 or 0
local miZoomScale = 140 * zoomTime * fps * radiusPix ^- 1.375
configCache.XTZoomTime = zoomTime
configCache.MIZoomInScale = max ( 0.25 , min ( 1.5 , miZoomScale ) )
configCache.MIZoomOutScale = max ( 0.4 , min ( 0.75 , 0.4 * miZoomScale ) )
configCache.MISpinOutProg = 150 * max ( 0.15 , min ( 1 , fps * fps * fps / 216000 ) )
configCache.MIScaleAdd = configCache.MIScale and ( radiusRaw > 200 and 0.05 or 0.10 ) or 0
mainFrame.eleft = anim == " fast-in " and 0.5 * zoomTime or zoomTime
mainFrame : SetScript ( " OnUpdate " , uf )
uf ( mainFrame , hadOnUpdate and 0 or 1 / 60 )
end
local function notifyAlpha ( p , wa , wc , ws )
local nf = ActiveIndicatorFactory.onParentAlphaChanged
local pea = nf and p : GetEffectiveAlpha ( )
for i = ws or 1 , nf and wc or 0 do
nf ( wa [ i ] , pea )
end
end
do -- GhostIndication
local spareGroups , spareSlices , currentGroups , visibleGroups , activeGroup = { } , { } , { } , { }
local function AnimateHide ( self , elapsed )
local et , zoomTime = self.expire , configCache.XTZoomTime
et = et and ( et - elapsed ) or zoomTime
if et <= 0 then
visibleGroups [ self ] , self.expire = nil
self : Hide ( )
else
self.expire = et
self : SetAlpha ( et <= zoomTime and et / zoomTime or 1 )
notifyAlpha ( self , self , self.count , 2 )
end
end
local function AnimateShow ( self , elapsed )
local et , zoomTime = self.expire , configCache.XTZoomTime
et = et and ( et - elapsed ) or ( zoomTime + configCache.GhostShowDelay )
if et <= 0 then
visibleGroups [ self ] , self.expire = false , nil
self : SetAlpha ( 1 )
else
self.expire = et
self : SetAlpha ( et > zoomTime and 0 or ( 1 - et / zoomTime ) )
end
notifyAlpha ( self , self , self.count , 2 )
end
local function newGhostGroup ( )
local f = CreateFrame ( " Frame " , nil , mainFrame )
f : SetSize ( 1 , 1 )
f : SetScale ( 0.80 )
f : Hide ( )
f [ 1 ] = 1
return f
end
function GhostIndication : ActivateGroup ( index , count , incidentAngle , mainRadius , mainScale )
local ret = currentGroups [ index ] or next ( spareGroups ) or newGhostGroup ( )
currentGroups [ index ] , spareGroups [ ret ] = ret
if not ret : IsShown ( ) then
visibleGroups [ ret ] , ret.expire = AnimateShow , nil
AnimateShow ( ret , 0 )
ret : Show ( )
end
if activeGroup ~= ret then GhostIndication : Deactivate ( ) end
if ret.incident ~= incidentAngle or ret.count ~= count then
local baseSize = 48 + 48 * configCache.MIButtonMargin
local radius , angleStep = CalculateRingRadius ( count , baseSize * mainScale , 48 * 0.80 , 30 , incidentAngle - 180 ) / 0.80 , 360 / count
local angle = incidentAngle - angleStep + 90
for i = 2 , count do
local cell = ret [ i ] or next ( spareSlices ) or CreateIndicator ( nil , ret , 48 , true )
cell : SetParent ( ret )
SetAngle ( cell , angle , radius )
cell : SetShown ( true )
ret [ i ] , angle , spareSlices [ cell ] = cell , angle - angleStep
end
for i = count + 1 , ret.count or 0 do
local cell = ret [ i ]
cell : SetShown ( false )
spareSlices [ cell ] , ret [ i ] = cell , nil
end
ret.incident , ret.count = incidentAngle , count
ret : SetPoint ( " CENTER " , ( mainRadius / 0.80 + radius ) * cos ( incidentAngle ) , ( mainRadius / 0.80 + radius ) * sin ( incidentAngle ) )
ret : Show ( )
end
activeGroup = ret
return ret
end
function GhostIndication : Deactivate ( )
if activeGroup then
visibleGroups [ activeGroup ] , activeGroup.expire = AnimateHide , nil
activeGroup = nil
end
end
function GhostIndication : Reset ( )
for k , g in pairs ( currentGroups ) do
g : Hide ( )
for i = 2 , g.count or 0 do
g [ i ] : SetShown ( false )
spareSlices [ g [ i ] ] , g [ i ] = g [ i ]
end
spareGroups [ g ] , currentGroups [ k ] , visibleGroups [ g ] , g.incident , g.count = g
end
activeGroup = nil
end
function GhostIndication : SwitchSparePool ( pool )
self : Reset ( )
spareSlices = pool
end
function GhostIndication : OnUpdate ( elapsed )
for g , af in pairs ( visibleGroups ) do
if af then af ( g , elapsed ) end
end
end
function GhostIndication : NotifyAlpha ( )
for g in pairs ( visibleGroups ) do
notifyAlpha ( g , g , g.count , 2 )
end
end
end
local SwitchIndicatorFactory , ValidateIndicator do
local CURRENT_API_LEVEL , CURRENT_API_LEVEL_OOD = 2 , MODERN and 2 or 1
local RequiredIndicatorMethods = {
SetPoint = 0 , SetScale = 0 , GetScale = 0 , SetShown = 0 , SetParent = 0 ,
SetIcon = 0 , SetIconTexCoord = 0 , SetIconVertexColor = 0 , SetDominantColor = 0 ,
SetOverlayIcon = 0 , SetOverlayIconVertexColor = 1 ,
SetUsable = 0 , SetCount = 0 , SetBinding = 0 ,
SetCooldown = 0 , SetCooldownTextShown = " supportsCooldownNumbers " , SetShortLabel = " supportsShortLabels " ,
SetEquipState = 0 , SetHighlighted = 0 , SetActive = 0 , SetOuterGlow = 0 ,
SetQualityOverlay = 2 ,
}
function ValidateIndicator ( apiLevel , reqAPILevel , info , errorLevel )
if apiLevel < 0 or ( reqAPILevel or apiLevel ) > CURRENT_API_LEVEL then
error ( " API level " .. apiLevel .. " is not supported (current is " .. CURRENT_API_LEVEL .. " ) " , errorLevel )
end
local f = info.CreateIndicator ( nil , mainFrame , 48 )
for k , v in pairs ( RequiredIndicatorMethods ) do
local tv = type ( v )
if type ( f [ k ] ) ~= " function " and ( ( tv == " number " and apiLevel >= v ) or ( tv == " string " and info [ v ] ) ) then
return error ( ( " Expected a function for indicator key %q, got %s. " ) : format ( k , type ( f [ k ] ) ) , errorLevel )
end
end
return { [ f ] = true }
end
function SwitchIndicatorFactory ( iakey )
local iakey = ( iakey == nil or iakey == " _ " ) and LastRegisteredIndicatorFactory or iakey
local finfo = IndicatorFactories [ iakey ] or IndicatorFactories.mirage
if finfo ~= ActiveIndicatorFactory then
local oldPool = ActiveIndicatorFactory and ActiveIndicatorFactory.mainPool
for k , v in pairs ( Slices ) do
oldPool [ v ] , Slices [ k ] = true , nil
v : SetShown ( false )
end
mainFrame : Hide ( )
CreateIndicator , ActiveIndicatorFactory = finfo.CreateIndicator , finfo
GhostIndication : SwitchSparePool ( finfo.ghostPool )
end
end
local function nextRIC ( _ , key )
if key == " _ " then return end
local nk , nv = next ( IndicatorFactories , key )
if nk then
return nk , nv.name , nv.apiLevel < CURRENT_API_LEVEL_OOD
end
return " _ " , IndicatorFactories [ LastRegisteredIndicatorFactory ] . name , false
end
function iapi : EnumerateRegisteredIndicatorConstructors ( )
return nextRIC
end
function iapi : DoesIndicatorConstructorSupport ( key , feature )
if key == nil or key == " _ " then key = LastRegisteredIndicatorFactory end
if not IndicatorFactories [ key ] then key = LastRegisteredIndicatorFactory or " mirage " end
return not not IndicatorFactories [ key ] [ " supports " .. feature ]
end
function iapi : GetIndicatorConstructorName ( key )
if key == nil or key == " _ " then key = LastRegisteredIndicatorFactory end
local ic = IndicatorFactories [ key ]
return ic and ic.name
end
function iapi : HasMultipleIndicatorConstructors ( )
return nil ~= next ( IndicatorFactories , ( next ( IndicatorFactories ) ) )
end
end
local tokenR , tokenG , tokenB , tokenIcon , tokenQuest = { } , { } , { } , { } , { }
local qualMap , qualMod , qualModLow = { } , 131072 , 16384 do
for v = qualModLow , qualMod - 1 , qualModLow do
qualMap [ v ] = v / qualModLow
end
end
local getSliceColor do
local col , pal = T.Niji . _tex , T.Niji . _palette
function getSliceColor ( token , icon , token2 )
if tokenR [ token ] then return tokenR [ token ] , tokenG [ token ] , tokenB [ token ] end
if tokenR [ token2 ] then return tokenR [ token2 ] , tokenG [ token2 ] , tokenB [ token2 ] end
local li = col [ icon ] or - 3
return pal [ li ] or 0.7 , pal [ li + 1 ] or 1 , pal [ li + 2 ] or 0.6
end
end
local function extractAux ( ext , v )
if not ext then
elseif v == " coord " then
local t = type ( ext.iconCoords )
if t == " table " then
return unpack ( ext.iconCoords )
elseif t == " function " then
return ext.iconCoords ( )
end
elseif v == " color " and type ( ext.iconR ) == " number " and type ( ext.iconG ) == " number " and type ( ext.iconB ) == " number " then
return ext.iconR , ext.iconG , ext.iconB
end
end
local function SetDefaultAnchor ( tt , owner )
if tt : IsOwned ( owner ) then
tt : ClearLines ( )
else
GameTooltip_SetDefaultAnchor ( tt , owner )
end
end
local function updateCentralElements ( self , si , _ , tok , usable , state , icon , caption , _ , _ , _ , tipFunc , tipArg , _ , stext )
local osi = self.oldSlice
if tok then
local r , g , b = getSliceColor ( tok , tokenIcon [ tok ] or icon or " Interface/Icons/INV_Misc_QuestionMark " )
centerPointer : SetVertexColor ( r , g , b , 0.9 )
centerCircle : SetVertexColor ( r , g , b , 0.9 )
centerGlow : SetVertexColor ( r , g , b )
elseif si ~= osi then
centerPointer : SetVertexColor ( 1 , 1 , 1 , 0.1 )
centerCircle : SetVertexColor ( 1 , 1 , 1 , 0.3 )
centerGlow : SetVertexColor ( 0.75 , 0.75 , 0.75 )
end
if configCache.UseGameTooltip then
if tipFunc and tipArg then
SetDefaultAnchor ( GameTooltip , mainFrame )
tipFunc ( GameTooltip , tipArg )
GameTooltip : Show ( )
else
local text = caption and caption ~= " " and caption or stext
if text then
SetDefaultAnchor ( GameTooltip , mainFrame )
GameTooltip : AddLine ( text )
GameTooltip : Show ( )
elseif GameTooltip : IsOwned ( mainFrame ) then
GameTooltip : Hide ( )
end
end
end
local sm = ( state and ( state % 4 > 1 ) and 0.625 or 1 )
if self.rotPeriod ~= sm then
self.rotPeriod = sm
setRingRotationPeriod ( configCache.XTRotationPeriod * sm )
end
local gAnim , gEnd , oIG , time , usable = self.gAnim , self.gEnd , self.oldIsGlowing , GetTime ( ) , usable or ( state and usable ~= false ) or false
if usable ~= oIG then
gAnim , gEnd = usable and " in " or " out " , time + 0.3 - ( gEnd and gEnd > time and ( gEnd - time ) or 0 )
self.oldIsGlowing , self.gAnim , self.gEnd = usable , gAnim , gEnd
centerGlow : SetShown ( true )
end
if gAnim and gEnd <= time or oIG == nil then
self.gAnim , self.gEnd = nil , nil
centerGlow : SetShown ( usable )
centerGlow : SetAlpha ( 0.75 )
elseif gAnim then
local pg = ( gEnd - time ) / 0.3 * 0.75
local a = usable and ( pg > 0.75 and 0 or ( 0.75 - pg ) ) or pg
centerGlow : SetAlpha ( a < 0 and 0 or a )
end
self.oldSlice = si
end
local function updateSlice ( self , originAngle , selected , tok , usable , state , icon , _ , count , cd , cd2 , _tf , _ta , ext , stext )
local isJump , origJumpIcon , jumpOtherTok , isJumpIconOverlay = false , icon
state , usable , ext = state or 0 , usable or ( state and usable ~= false ) or false , not tokenIcon [ tok ] and ext or nil
if state % 8192 >= 4096 then
icon , jumpOtherTok , isJump , count = 188515 , count , true , 0
end
icon = tokenIcon [ tok ] or icon or " Interface/Icons/INV_Misc_QuestionMark "
isJumpIconOverlay = isJump and icon == 188515
local active , overlay , faded , usableCharge , r , g , b = state % 2 >= 1 , state % 4 >= 2 , not usable , usable or ( state % 128 >= 64 )
local isInContainer , isInInventory , isQuestStartItem = state % 256 >= 128 , state % 512 >= 256 , tokenQuest [ tok ] or ( state % 64 >= 32 )
local isDisenchanting = state % 262144 >= 131072
local onCooldown , noMana , noRange , qual = cd and cd > 0 , state % 16 >= 8 , state % 32 >= 16 , state % qualMod
qual = qual >= qualModLow and qualMap [ qual - qual % qualModLow ] or 0
self : SetIcon ( icon )
if ext then
self : SetIconTexCoord ( securecall ( extractAux , ext , " coord " ) )
r , g , b = securecall ( extractAux , ext , " color " )
end
local dr , dg , db = getSliceColor ( tok , isJumpIconOverlay and origJumpIcon or icon , jumpOtherTok )
self : SetUsable ( usable , usableCharge , onCooldown , noMana , noRange )
self : SetIconVertexColor ( r or 1 , g or 1 , b or 1 )
self : SetDominantColor ( dr , dg , db )
self : SetOuterGlow ( overlay )
if isJumpIconOverlay then
local isNestedJump = state % 16384 >= 8192
local cx , cy , cr = 128 / 256 , 45 / 256 , 0.53 * 0.45 -- l, r, t, b = 97/256, 159/256, 14/256, 76/256
local a1 , x1 , x2 , x3 , x4 , y1 , y2 , y3 , y4 = ( isNestedJump and - 45 or 135 ) - originAngle
x1 , y1 = cx + cr * cos ( a1 ) , cy - cr * sin ( a1 )
x2 , y2 = cx + cr * cos ( a1 + 90 ) , cy - cr * sin ( a1 + 90 )
x3 , y3 = cx + cr * cos ( a1 + 180 ) , cy - cr * sin ( a1 + 180 )
x4 , y4 = cx + cr * cos ( a1 + 270 ) , cy - cr * sin ( a1 + 270 )
self : SetOverlayIcon ( gfxBase .. " pointer " , 40 , 40 , x2 , y2 , x3 , y3 , x1 , y1 , x4 , y4 )
if ActiveIndicatorFactory.apiLevel >= 1 then
self : SetOverlayIconVertexColor ( dr , dg , db )
end
elseif isDisenchanting then
self : SetOverlayIcon ( " Interface/Buttons/UI-GroupLoot-DE-Up " , 20 , 20 )
else
self : SetOverlayIcon ( isQuestStartItem and " Interface \\ MINIMAP \\ TRACKING \\ OBJECTICONS " , 21 , 28 , 40 / 256 , 64 / 256 , 32 / 64 , 1 )
end
if ActiveIndicatorFactory.supportsShortLabels then
self : SetShortLabel ( configCache.ShowShortLabels and stext or " " )
end
if ActiveIndicatorFactory.apiLevel >= 2 then
self : SetQualityOverlay ( qual )
end
self : SetCooldown ( cd , cd2 , usableCharge )
self : SetEquipState ( isInContainer , isInInventory )
local ct = configCache.ShowOneCount and 0 or 1
self : SetCount ( ( count or 0 ) > ct and count )
self : SetActive ( active )
self : SetHighlighted ( selected and not faded )
end
local ambiguateToken , wipeTokenCache do
local cache = { }
function ambiguateToken ( tok , ... )
local atok = cache [ tok ]
if atok == nil and type ( tok ) == " string " then
atok = tok : match ( " ^[^:]+ " )
cache [ tok ] = atok
end
return atok or tok , ...
end
function wipeTokenCache ( )
wipe ( cache )
end
end
local function callElementUpdate ( self , f , si , ni , a1 , a2 )
return true , f ( self , a1 , a2 , ambiguateToken ( PC : GetOpenRingSliceAction ( si , ni ) ) )
end
local lastConAngle = nil
local function OnUpdate_CheckAlpha ( self , count )
local ea = self : GetEffectiveAlpha ( )
if self.oldEA ~= ea then
self.oldEA = ea
notifyAlpha ( self , Slices , count )
GhostIndication : NotifyAlpha ( )
end
end
local function OnUpdate_Main ( self , elapsed )
local count , offset = self.count , self.offset
local imode , qaid , angle , isActiveRadius , stl = PC : GetCurrentInputs ( )
local radius , miScaleAdd , frameRate = self.radius , configCache.MIScaleAdd , GetFramerate ( )
if qaid and count > 0 then
angle = ( 90 - offset - ( qaid - 1 ) * 360 / count ) % 360
elseif imode == " stick " then
angle = stl < 0.25 and lastConAngle or angle
lastConAngle = angle
end
local oangle = qaid and angle or self.angle or angle
local adiff , arate = min ( ( angle - oangle ) % 360 , ( oangle - angle ) % 360 )
if adiff > 60 then
arate = 420 + 120 * sin ( min ( 90 , adiff - 60 ) )
elseif adiff > 15 then
arate = 180 + 240 * sin ( min ( 90 , max ( ( adiff - 15 ) * 2 , 0 ) ) )
else
arate = 20 + 160 * sin ( min ( 90 , adiff * 6 ) )
end
local abound = configCache.XTPointerSnap and 360 or ( 1.25 * arate / frameRate )
local arotDirection = ( ( oangle - angle ) % 360 < ( angle - oangle ) % 360 ) and - 1 or 1
self.angle = ( adiff < abound or frameRate < MIN_ANIMATION_FPS ) and angle or ( oangle + arotDirection * abound ) % 360
centerPointer : SetRotation ( self.angle / 180 * 3.1415926535898 - 90 / 180 * 3.1415926535898 )
local si = qaid or ( count <= 0 and 0 ) or isActiveRadius and
( floor ( ( ( 90 - angle - offset ) * count / 360 + 0.5 ) % count ) + 1 ) or 0
securecall ( callElementUpdate , self , updateCentralElements , si , nil , si )
if count == 0 then
return
elseif miScaleAdd > 0 then
local limit = frameRate >= 40 and 10 * miScaleAdd / frameRate or miScaleAdd
for i = 1 , count do
local s , new = Slices [ i ] , i == si and miScaleAdd + 1 or 1
local old = s : GetScale ( )
s : SetScale ( old + min ( limit , max ( - limit , new - old ) ) )
end
end
OnUpdate_CheckAlpha ( self , count )
local cmState , mut = ( IsShiftKeyDown ( ) and 1 or 0 ) + ( IsControlKeyDown ( ) and 2 or 0 ) + ( IsAltKeyDown ( ) and 4 or 0 ) + ( MODERN and IsMetaKeyDown ( ) and 8 or 0 ) , self.schedMultiUpdate or 0
if self.omState == cmState and mut < 0 then
self.schedMultiUpdate = mut + elapsed
else
self.omState , self.schedMultiUpdate = cmState , - 0.05
for i = 1 , count do
local originAngle = 90 - ( i - 1 ) * 360 / count - offset
securecall ( callElementUpdate , Slices [ i ] , updateSlice , i , nil , originAngle , si == i )
end
if configCache.GhostMIRings then
local _ , _ , _ , nestedCount , atype , isNested = PC : GetOpenRingSlice ( si or 0 )
if ( nestedCount or 0 ) == 0 then
GhostIndication : Deactivate ( )
else
local jump1 = ( atype == " jump " and not isNested ) and 1 or 0
local originAngle , nestAngleStep = 90 - 360 / count * ( si - 1 ) - offset , 360 / ( nestedCount + jump1 )
local nestAngleBase = 180 + originAngle + ( 1 - jump1 ) * nestAngleStep
local group = GhostIndication : ActivateGroup ( si , nestedCount + jump1 , originAngle , radius * ( miScaleAdd + 1 ) , 1.10 )
for i = 2 - jump1 , nestedCount do
securecall ( callElementUpdate , group [ i + jump1 ] , updateSlice , si , i , nestAngleBase - nestAngleStep * i , false )
end
end
end
end
GhostIndication : OnUpdate ( elapsed )
end
local function OnUpdate_ZoomIn ( self , elapsed )
local r , sm , a = self.eleft - elapsed
self.eleft , r = r , r > 0 and r / configCache.XTZoomTime or 0
if r == 0 then self : SetScript ( " OnUpdate " , OnUpdate_Main ) end
sm = 1 + configCache.MIZoomInScale * 0.375 * r / ( 1.375 - r )
a = r > 0.4 and 1 - ( r - 0.4 ) / 0.6 or 1
self : SetScale ( configCache.RingScale * sm )
self : SetAlpha ( a < 0 and 0 or a )
return OnUpdate_Main ( self , elapsed )
end
local function OnUpdate_ZoomOut ( self , elapsed )
local r = self.eleft - elapsed
self.eleft , r = r , r > 0 and r / configCache.XTZoomTime or 0
if r <= 0 then
self : Hide ( )
self : SetScript ( " OnUpdate " , nil )
return
elseif configCache.MISpinOnHide then
local count = self.count
if count > 0 then
local sliceAngle , angleStep , radius , prog = 45 - self.offset + 45 * r , 360 / count , self.radius , ( 1 - r ) * configCache.MISpinOutProg
for i = 1 , count do
Slices [ i ] : SetPoint ( " CENTER " , cos ( sliceAngle ) * radius + cos ( sliceAngle - 90 ) * prog , sin ( sliceAngle ) * radius + sin ( sliceAngle - 90 ) * prog )
sliceAngle = sliceAngle - angleStep
end
end
self : SetScale ( configCache.RingScale * ( 1 + configCache.MIZoomOutScale * ( 1 - r ) ) )
else
self : SetScale ( configCache.RingScale * r )
end
self : SetAlpha ( r < 1 and r or 1 )
OnUpdate_CheckAlpha ( self , self.count )
GhostIndication : OnUpdate ( elapsed )
end
mainFrame : SetScript ( " OnHide " , function ( self )
if self : IsShown ( ) and self : GetScript ( " OnUpdate " ) == OnUpdate_ZoomOut then
self : SetScript ( " OnUpdate " , nil )
self : Hide ( )
end
end )
function iapi : Show ( _ , _ , fastOpen )
local _ , count , offset = PC : GetOpenRing ( configCache )
local baseSize , radius = 48 + 48 * configCache.MIButtonMargin
radius = CalculateRingRadius ( count or 3 , baseSize , baseSize , 100 , 90 - ( offset or 0 ) )
mainFrame.count , mainFrame.offset , mainFrame.radius = count , offset , radius
mainFrame.oldSlice , mainFrame.angle , mainFrame.omState , mainFrame.oldIsGlowing , mainFrame.rotPeriod , lastConAngle , mainFrame.oldEA = - 1
GhostIndication : Reset ( )
SwitchIndicatorFactory ( configCache.IndicatorFactory )
local astep = count == 0 and 0 or - 360 / count
for i = 1 , count do
local indic , _ , _ , sliceBind = Slices [ i ] or rawset ( Slices , i , next ( ActiveIndicatorFactory.mainPool ) or CreateIndicator ( nil , mainFrame , 48 ) ) [ i ] , PC : GetOpenRingSlice ( i )
ActiveIndicatorFactory.mainPool [ indic ] = nil
indic : SetBinding ( configCache.ShowKeys and sliceBind or nil )
SetAngle ( indic , ( i - 1 ) * astep - mainFrame.offset , radius )
if ActiveIndicatorFactory.supportsCooldownNumbers then
indic : SetCooldownTextShown ( configCache.ShowCooldowns , configCache.ShowRecharge )
end
indic : SetShown ( true )
indic : SetScale ( 1 )
end
for i = count + 1 , # Slices do
Slices [ i ] : SetShown ( false )
end
configCache.RingScale = max ( 0.1 , configCache.RingScale )
mainFrame : SetScale ( configCache.RingScale )
if fastOpen == " inplace-switch " then
elseif configCache.RingAtMouse then
local cx , cy = GetCursorPosition ( )
anchorFrame : SetPoint ( " CENTER " , nil , " BOTTOMLEFT " , cx + configCache.IndicationOffsetX , cy - configCache.IndicationOffsetY )
else
anchorFrame : SetPoint ( " CENTER " , nil , " CENTER " , configCache.IndicationOffsetX , - configCache.IndicationOffsetY )
end
SetupTransitionAnimation ( fastOpen and " fast-in " or " in " , OnUpdate_ZoomIn )
mainFrame : Show ( )
end
function iapi : Hide ( )
SetupTransitionAnimation ( " out " , OnUpdate_ZoomOut )
GhostIndication : Deactivate ( )
if GameTooltip : IsOwned ( mainFrame ) then
GameTooltip : Hide ( )
end
wipeTokenCache ( )
end
function api : SetDisplayOptions ( token , icon , _ , r , g , b )
if type ( r ) ~= " number " or type ( g ) ~= " number " or type ( b ) ~= " number " then r , g , b = nil end
tokenR [ token ] , tokenG [ token ] , tokenB [ token ] , tokenIcon [ token ] = r , g , b , icon
end
function api : SetQuestHint ( sliceToken , hint )
tokenQuest [ sliceToken ] = hint or nil
end
function api : GetTexColor ( icon )
return getSliceColor ( nil , icon )
end
function api : RegisterIndicatorConstructor ( key , info )
assert ( type ( key ) == " string " and type ( info ) == " table " , ' Syntax: OPieUI:RegisterIndicatorConstructor("key", infoTable) ' , 2 )
local func , apiLevel , iname , reqAPILevel = info.CreateIndicator , info.apiLevel , info.name , info.reqAPILevel
local onPAC = info.onParentAlphaChanged
assert ( key ~= " _ " and IndicatorFactories [ key ] == nil , ' RegisterIndicatorConstructor: an indicator constructor with the specified key is already registered ' , 2 )
assert ( type ( func ) == " function " , ' RegisterIndicatorConstructor: info.CreateIndicator must be a function ' , 2 )
assert ( type ( apiLevel ) == " number " and apiLevel < math.huge , ' RegisterIndicatorConstructor: info.apiLevel must be a finite number ' , 2 )
assert ( type ( iname ) == " string " , ' RegisterIndicatorConstructor: info.name must be a string ' , 2 )
assert ( type ( reqAPILevel ) == " number " or reqAPILevel == nil , ' RegisterIndicatorConstructor: info.reqAPILevel, if set, must be a number ' , 2 )
assert ( type ( onPAC ) == " function " or onPAC == nil , ' RegisterIndicatorConstructor: info.onParentAlphaChanged, if set, must be a function ' , 2 )
local mainPool = ValidateIndicator ( apiLevel , reqAPILevel , info , 3 )
LastRegisteredIndicatorFactory , IndicatorFactories [ key ] = key , {
name = iname : gsub ( " |+ " , " " ) ,
apiLevel = apiLevel ,
CreateIndicator = func ,
mainPool = mainPool ,
ghostPool = { } ,
supportsCooldownNumbers = not not info.supportsCooldownNumbers ,
supportsShortLabels = not not info.supportsShortLabels ,
onParentAlphaChanged = onPAC ,
}
end
for k , v in pairs ( { IndicatorFactory = " _ " ,
ShowCooldowns = false , ShowRecharge = false , UseGameTooltip = true , ShowKeys = true , ShowOneCount = false , ShowShortLabels = true ,
MIScale = true , MISpinOnHide = true , MIButtonMargin = 0.1 , GhostMIRings = true ,
XTPointerSnap = false , XTAnimation = true , XTRotationPeriod = 4 , GhostShowDelay = 0.25 } ) do
PC : RegisterOption ( k , v )
end
api : RegisterIndicatorConstructor ( " mirage " , T.Mirage )
T.OPieUI , OPie.UI = iapi , api