Need help on Lua Code For Health, Power, Mana

This is a ring code and I’m trying to understand where I would add the textures in and also where to add the code to have it work for Health, Mana, and Power for both player and Target.

-- Usage:
-- spinner = CreateSpinner(parent)
-- spinner:SetTexture('texturePath')
-- spinner:SetBlendMode('blendMode')
-- spinner:SetVertexColor(r, g, b)
-- spinner:SetClockwise(boolean) -- true to fill clockwise, false to fill counterclockwise
-- spinner:SetReverse(boolean) -- true to empty the bar instead of filling it
-- spinner:SetValue(percent) -- value between 0 and 1 to fill the bar to
 
-- Some math stuff
local cos, sin, pi2, halfpi = math.cos, math.sin, math.rad(360), math.rad(90)
local function Transform(tx, x, y, angle, aspect) -- Translates texture to x, y and rotates about its center
    local c, s = cos(angle), sin(angle)
    local y, oy = y / aspect, 0.5 / aspect
    local ULx, ULy = 0.5 + (x - 0.5) * c - (y - oy) * s, (oy + (y - oy) * c + (x - 0.5) * s) * aspect
    local LLx, LLy = 0.5 + (x - 0.5) * c - (y + oy) * s, (oy + (y + oy) * c + (x - 0.5) * s) * aspect
    local URx, URy = 0.5 + (x + 0.5) * c - (y - oy) * s, (oy + (y - oy) * c + (x + 0.5) * s) * aspect
    local LRx, LRy = 0.5 + (x + 0.5) * c - (y + oy) * s, (oy + (y + oy) * c + (x + 0.5) * s) * aspect
    tx:SetTexCoord(ULx, ULy, LLx, LLy, URx, URy, LRx, LRy)
end
 
-- Permanently pause our rotation animation after it starts playing
local function OnPlayUpdate(self)
    self:SetScript('OnUpdate', nil)
    self:Pause()
end
 
local function OnPlay(self)
    self:SetScript('OnUpdate', OnPlayUpdate)
end
 
local function SetValue(self, value)
    -- Correct invalid ranges, preferably just don't feed it invalid numbers
    if value > 1 then value = 1
    elseif value < 0 then value = 0 end
    
    -- Reverse our normal behavior
    if self._reverse then
        value = 1 - value
    end
    
    -- Determine which quadrant we're in
    local q, quadrant = self._clockwise and (1 - value) or value -- 4 - floor(value / 0.25)
    if q >= 0.75 then
        quadrant = 1
    elseif q >= 0.5 then
        quadrant = 2
    elseif q >= 0.25 then
        quadrant = 3
    else
        quadrant = 4
    end
    
    if self._quadrant ~= quadrant then
        self._quadrant = quadrant
        -- Show/hide necessary textures if we need to
        if self._clockwise then
            for i = 1, 4 do
                self._textures[i]:SetShown(i < quadrant)
            end
        else
            for i = 1, 4 do
                self._textures[i]:SetShown(i > quadrant)
            end
        end
        -- Move scrollframe/wedge to the proper quadrant
        self._scrollframe:SetAllPoints(self._textures[quadrant])    
    end
 
    -- Rotate the things
    local rads = value * pi2
    if not self._clockwise then rads = -rads + halfpi end
    Transform(self._wedge, -0.5, -0.5, rads, self._aspect)
    self._rotation:SetRadians(-rads)
end
 
local function SetClockwise(self, clockwise)
    self._clockwise = clockwise
end
 
local function SetReverse(self, reverse)
    self._reverse = reverse
end
 
local function OnSizeChanged(self, width, height)
    self._wedge:SetSize(width, height) -- it's important to keep this texture sized correctly
    self._aspect = width / height -- required to calculate the texture coordinates
end
 
-- Creates a function that calls a method on all textures at once
local function CreateTextureFunction(func, self, ...)
    return function(self, ...)
        for i = 1, 4 do
            local tx = self._textures[i]
            tx[func](tx, ...)
        end
        self._wedge[func](self._wedge, ...)
    end
end
 
-- Pass calls to these functions on our frame to its textures
local TextureFunctions = {
    SetTexture = CreateTextureFunction('SetTexture'),
    SetBlendMode = CreateTextureFunction('SetBlendMode'),
    SetVertexColor = CreateTextureFunction('SetVertexColor'),
}
 
local function CreateSpinner(parent)
    local spinner = CreateFrame('Frame', nil, parent)
    
    -- ScrollFrame clips the actively animating portion of the spinner
    local scrollframe = CreateFrame('ScrollFrame', nil, spinner)
    scrollframe:SetPoint('BOTTOMLEFT', spinner, 'CENTER')
    scrollframe:SetPoint('TOPRIGHT')
    spinner._scrollframe = scrollframe
    
    local scrollchild = CreateFrame('frame', nil, scrollframe)
    scrollframe:SetScrollChild(scrollchild)
    scrollchild:SetAllPoints(scrollframe)
    
    -- Wedge thing
    local wedge = scrollchild:CreateTexture()
    wedge:SetPoint('BOTTOMRIGHT', spinner, 'CENTER')
    spinner._wedge = wedge
    
    -- Top Right
    local trTexture = spinner:CreateTexture()
    trTexture:SetPoint('BOTTOMLEFT', spinner, 'CENTER')
    trTexture:SetPoint('TOPRIGHT')
    trTexture:SetTexCoord(0.5, 1, 0, 0.5)
    
    -- Bottom Right
    local brTexture = spinner:CreateTexture()
    brTexture:SetPoint('TOPLEFT', spinner, 'CENTER')
    brTexture:SetPoint('BOTTOMRIGHT')
    brTexture:SetTexCoord(0.5, 1, 0.5, 1)
    
    -- Bottom Left
    local blTexture = spinner:CreateTexture()
    blTexture:SetPoint('TOPRIGHT', spinner, 'CENTER')
    blTexture:SetPoint('BOTTOMLEFT')
    blTexture:SetTexCoord(0, 0.5, 0.5, 1)
    
    -- Top Left
    local tlTexture = spinner:CreateTexture()
    tlTexture:SetPoint('BOTTOMRIGHT', spinner, 'CENTER')
    tlTexture:SetPoint('TOPLEFT')
    tlTexture:SetTexCoord(0, 0.5, 0, 0.5)
    
    -- /4|1\ -- Clockwise texture arrangement
    -- \3|2/ --
 
    spinner._textures = {trTexture, brTexture, blTexture, tlTexture}
    spinner._quadrant = nil -- Current active quadrant
    spinner._clockwise = true -- fill clockwise
    spinner._reverse = false -- Treat the provided value as its inverse, eg. 75% will display as 25%
    spinner._aspect = 1 -- aspect ratio, width / height of spinner frame
    spinner:HookScript('OnSizeChanged', OnSizeChanged)
    
    for method, func in pairs(TextureFunctions) do
        spinner[method] = func
    end
    
    spinner.SetClockwise = SetClockwise
    spinner.SetReverse = SetReverse
    spinner.SetValue = SetValue
    
    local group = wedge:CreateAnimationGroup()
    local rotation = group:CreateAnimation('Rotation')
    spinner._rotation = rotation
    rotation:SetDuration(0)
    rotation:SetEndDelay(1)
    rotation:SetOrigin('BOTTOMRIGHT', 0, 0)
    group:SetScript('OnPlay', OnPlay)
    group:Play()
    
    return spinner
end

If anyone knows this stuff better then I do it would sure help me out to finish up the player and target unit frames I made. Thank you!

Best to ask in the UI forum.

This would appear to be only the display class, you’d need to look at the code that creates the instance of it to see where value gets set.

This looks like it was built using the MVC paradigm, and you only have the code for the view.

That’s the best I could get from legendary Zork. He said

That is the ring technique used in WeakAuras today. It allows you to create a ring of any texture of your choosing. There may have been API adjustments in the meantime. I would dig into WeakAuras and grab their code for the ring generation.

Sadly I don’t have that much time anymore to dig into stuff myself.

Best of luck,
zork

This is the rest of the code but it just says Demo

-- Demo
----------
 
local spinner1 = CreateSpinner(UIParent)
spinner1:SetPoint('BOTTOMRIGHT', UIParent, 'CENTER', -2, 2)
spinner1:SetSize(64, 64)
spinner1:SetTexture('interface/icons/inv_mushroom_11')
 
spinner1:SetClockwise(false)
spinner1:SetReverse(false)
 
local spinner2 = CreateSpinner(UIParent)
spinner2:SetPoint('BOTTOMLEFT', UIParent, 'CENTER', 2, 2)
spinner2:SetSize(64, 64)
spinner2:SetTexture('interface/icons/inv_mushroom_11')
 
spinner2:SetClockwise(true)
spinner2:SetReverse(false)
 
local spinner3 = CreateSpinner(UIParent)
spinner3:SetPoint('TOPRIGHT', UIParent, 'CENTER', -2, -2)
spinner3:SetSize(64, 64)
spinner3:SetTexture('interface/icons/inv_mushroom_11')
 
spinner3:SetClockwise(true)
spinner3:SetReverse(true)
 
local spinner4 = CreateSpinner(UIParent)
spinner4:SetPoint('TOPLEFT', UIParent, 'CENTER', 2, -2)
spinner4:SetSize(64, 64)
spinner4:SetTexture('interface/icons/inv_mushroom_11')
 
spinner4:SetClockwise(false)
spinner4:SetReverse(true)
 
local f = CreateFrame('frame')
local timespent = 0
f:SetScript('OnUpdate', function(self, elapsed)
    timespent = timespent + elapsed
    if timespent >= 3 then
        timespent = 0
    end
    
    local value = timespent / 3
    spinner1:SetValue(value)
    spinner2:SetValue(value)
    spinner3:SetValue(value)
    spinner4:SetValue(value)
end)

If this is code from weakauras, the code that gets the values for things to display are going to be in a completely different file

This looks like MVC, and you’re asking a controller question with code from the view

I think you’ll find Semlar’s code is (maybe was but probably still current) being used in WA (I’m not sure of the code cited so maybe it’s all rooted in the same core code).

It stems (maybe not directly) from this thread and it still works

Yeah that’s what Zork said it’s Semlar’s code. Otherwise that’s the best he could do to help me out or point me the right direction. Or using OUF and making a layout but I’m pretty stumped on this one by myself. I’m so close to making the unit frames we made independent from WeakAuras it’s just the Health, Power and Mana frames getting in the way that unfortunately involves a ring spinner to it.

I was able to remove one add-on already from the unit frames adding lua code till 8am
until I got it right. I had to hook some more frames and setpoint stuff. But it’s working good.

If you would like to help complete this with me Fizz I’ll share the U.I after I set it all up so you can check it out yourself and vise versa. I need to work getting the background positioned on the player frame texture so it’s a whole frame. ( I was using WeakAuras for the background at the moment so I could line up everything in place for now.

My plan was using the ring 20px texture on WeakAuras for health
Ring 40px for Mana
And the Dragon texture I made for power.

"I wonder what the non-pathetic people are doing tonight?" - Rajesh Koothrappali (The Big Bang Theory)

lol

The demo is using a timed 3 second rotation. Just change it to calculate the values based on health etc. This should work for target health (very basic):

-- Usage:
-- spinner = CreateSpinner(parent)
-- spinner:SetTexture('texturePath')
-- spinner:SetBlendMode('blendMode')
-- spinner:SetVertexColor(r, g, b)
-- spinner:SetClockwise(boolean) -- true to fill clockwise, false to fill counterclockwise
-- spinner:SetReverse(boolean) -- true to empty the bar instead of filling it
-- spinner:SetValue(percent) -- value between 0 and 1 to fill the bar to
 
-- Some math stuff
local cos, sin, pi2, halfpi = math.cos, math.sin, math.rad(360), math.rad(90)
local function Transform(tx, x, y, angle, aspect) -- Translates texture to x, y and rotates about its center
    local c, s = cos(angle), sin(angle)
    local y, oy = y / aspect, 0.5 / aspect
    local ULx, ULy = 0.5 + (x - 0.5) * c - (y - oy) * s, (oy + (y - oy) * c + (x - 0.5) * s) * aspect
    local LLx, LLy = 0.5 + (x - 0.5) * c - (y + oy) * s, (oy + (y + oy) * c + (x - 0.5) * s) * aspect
    local URx, URy = 0.5 + (x + 0.5) * c - (y - oy) * s, (oy + (y - oy) * c + (x + 0.5) * s) * aspect
    local LRx, LRy = 0.5 + (x + 0.5) * c - (y + oy) * s, (oy + (y + oy) * c + (x + 0.5) * s) * aspect
    tx:SetTexCoord(ULx, ULy, LLx, LLy, URx, URy, LRx, LRy)
end
 
-- Permanently pause our rotation animation after it starts playing
local function OnPlayUpdate(self)
    self:SetScript('OnUpdate', nil)
    self:Pause()
end
 
local function OnPlay(self)
    self:SetScript('OnUpdate', OnPlayUpdate)
end
 
local function SetValue(self, value)
    -- Correct invalid ranges, preferably just don't feed it invalid numbers
    if value > 1 then value = 1
    elseif value < 0 then value = 0 end
    
    -- Reverse our normal behavior
    if self._reverse then
        value = 1 - value
    end
    
    -- Determine which quadrant we're in
    local q, quadrant = self._clockwise and (1 - value) or value -- 4 - floor(value / 0.25)
    if q >= 0.75 then
        quadrant = 1
    elseif q >= 0.5 then
        quadrant = 2
    elseif q >= 0.25 then
        quadrant = 3
    else
        quadrant = 4
    end
    
    if self._quadrant ~= quadrant then
        self._quadrant = quadrant
        -- Show/hide necessary textures if we need to
        if self._clockwise then
            for i = 1, 4 do
                self._textures[i]:SetShown(i < quadrant)
            end
        else
            for i = 1, 4 do
                self._textures[i]:SetShown(i > quadrant)
            end
        end
        -- Move scrollframe/wedge to the proper quadrant
        self._scrollframe:SetAllPoints(self._textures[quadrant])    
    end
 
    -- Rotate the things
    local rads = value * pi2
    if not self._clockwise then rads = -rads + halfpi end
    Transform(self._wedge, -0.5, -0.5, rads, self._aspect)
    self._rotation:SetRadians(-rads)
end
 
local function SetClockwise(self, clockwise)
    self._clockwise = clockwise
end
 
local function SetReverse(self, reverse)
    self._reverse = reverse
end
 
local function OnSizeChanged(self, width, height)
    self._wedge:SetSize(width, height) -- it's important to keep this texture sized correctly
    self._aspect = width / height -- required to calculate the texture coordinates
end
 
-- Creates a function that calls a method on all textures at once
local function CreateTextureFunction(func, self, ...)
    return function(self, ...)
        for i = 1, 4 do
            local tx = self._textures[i]
            tx[func](tx, ...)
        end
        self._wedge[func](self._wedge, ...)
    end
end
 
-- Pass calls to these functions on our frame to its textures
local TextureFunctions = {
    SetTexture = CreateTextureFunction('SetTexture'),
    SetBlendMode = CreateTextureFunction('SetBlendMode'),
    SetVertexColor = CreateTextureFunction('SetVertexColor'),
}
 
local function CreateSpinner(parent)
    local spinner = CreateFrame('Frame', nil, parent)
    
    -- ScrollFrame clips the actively animating portion of the spinner
    local scrollframe = CreateFrame('ScrollFrame', nil, spinner)
    scrollframe:SetPoint('BOTTOMLEFT', spinner, 'CENTER')
    scrollframe:SetPoint('TOPRIGHT')
    spinner._scrollframe = scrollframe
    
    local scrollchild = CreateFrame('frame', nil, scrollframe)
    scrollframe:SetScrollChild(scrollchild)
    scrollchild:SetAllPoints(scrollframe)
    
    -- Wedge thing
    local wedge = scrollchild:CreateTexture()
    wedge:SetPoint('BOTTOMRIGHT', spinner, 'CENTER')
    spinner._wedge = wedge
    
    -- Top Right
    local trTexture = spinner:CreateTexture()
    trTexture:SetPoint('BOTTOMLEFT', spinner, 'CENTER')
    trTexture:SetPoint('TOPRIGHT')
    trTexture:SetTexCoord(0.5, 1, 0, 0.5)
    
    -- Bottom Right
    local brTexture = spinner:CreateTexture()
    brTexture:SetPoint('TOPLEFT', spinner, 'CENTER')
    brTexture:SetPoint('BOTTOMRIGHT')
    brTexture:SetTexCoord(0.5, 1, 0.5, 1)
    
    -- Bottom Left
    local blTexture = spinner:CreateTexture()
    blTexture:SetPoint('TOPRIGHT', spinner, 'CENTER')
    blTexture:SetPoint('BOTTOMLEFT')
    blTexture:SetTexCoord(0, 0.5, 0.5, 1)
    
    -- Top Left
    local tlTexture = spinner:CreateTexture()
    tlTexture:SetPoint('BOTTOMRIGHT', spinner, 'CENTER')
    tlTexture:SetPoint('TOPLEFT')
    tlTexture:SetTexCoord(0, 0.5, 0, 0.5)
    
    -- /4|1\ -- Clockwise texture arrangement
    -- \3|2/ --
 
    spinner._textures = {trTexture, brTexture, blTexture, tlTexture}
    spinner._quadrant = nil -- Current active quadrant
    spinner._clockwise = true -- fill clockwise
    spinner._reverse = false -- Treat the provided value as its inverse, eg. 75% will display as 25%
    spinner._aspect = 1 -- aspect ratio, width / height of spinner frame
    spinner:HookScript('OnSizeChanged', OnSizeChanged)
    
    for method, func in pairs(TextureFunctions) do
        spinner[method] = func
    end
    
    spinner.SetClockwise = SetClockwise
    spinner.SetReverse = SetReverse
    spinner.SetValue = SetValue
    
    local group = wedge:CreateAnimationGroup()
    local rotation = group:CreateAnimation('Rotation')
    spinner._rotation = rotation
    rotation:SetDuration(0)
    rotation:SetEndDelay(1)
    rotation:SetOrigin('BOTTOMRIGHT', 0, 0)
    group:SetScript('OnPlay', OnPlay)
    group:Play()
    
    return spinner
end

local spinner1 = CreateSpinner(UIParent)
spinner1:SetPoint('CENTER', UIParent)
spinner1:SetSize(64, 64)
spinner1:SetTexture("Interface\\PlayerFrame\\UI-PlayerFrame-Deathknight-Blood")
spinner1:SetClockwise(false)
spinner1:SetReverse(false)
spinner1:Hide()

local healthmax = 0
local health = 0
spinner1:RegisterUnitEvent("UNIT_HEALTH", target)
spinner1:RegisterEvent("PLAYER_TARGET_CHANGED")
spinner1:SetScript("OnEvent", function(sxelf, event, ...)
	if event == "PLAYER_TARGET_CHANGED" then
		if not UnitExists("target") then
			spinner1:Hide()
			return
		end
		spinner1:Show()
	end
	healthmax = UnitHealthMax("target")
	health = UnitHealth("target")
	local h = health / healthmax
	spinner1:SetValue(h)
end)

Oh wow cool! I’ll try it out after work and see what happens. Thanks for coming through Fizz!
This will help me out a lot :pray:

Everything from

local spinner1 = CreateSpinner(UIParent)

down can be re-used to create additional spinners and the actual event handling doesn’t have to be done by each individual frame, but that’s up to you.

I have the Health ring and background set in place and was able to change the target frame strata. What I’m attempting to do now is have a spinner for just units with mana but I’m having a hard time understanding how to get that to work. I was able to make a spinner on my own for just power and it’s working in place so that’s good.

I did do some research on Enum.PowerType.Mana but it’s confusing putting it into code and not sure if blizzard is still using it or not. This is the code I got to work for power.

local spinner2 = CreateSpinner(UIParent)
spinner2:SetPoint('CENTER', TargetFrame.TargetFrameContainer.Portrait)
spinner2:SetSize(158, 148)
spinner2:SetTexture("Interface\\Addons\\Images\\DragonCircleBackgroundTwo")
spinner2:SetVertexColor(1, 1, 1)
spinner2:SetClockwise(true)
spinner2:SetReverse(false)
spinner2:Hide()

local powermax = 0
local power = 0
spinner2:RegisterUnitEvent("UNIT_POWER_FREQUENT", target)
spinner2:RegisterEvent("PLAYER_TARGET_CHANGED")
spinner2:SetScript("OnEvent", function(self, event, ...)
	if event == "PLAYER_TARGET_CHANGED" then
		if not UnitExists("target") then
			spinner2:Hide()
			return
		end
		spinner2:Show()
	end
	powermax = UnitPowerMax("target")
	power = UnitPower("target")
	local p = power / powermax
	spinner2:SetValue(p)
end)

The only other stuff I’ll most likely will need help with is being able to change the start and end angles where the ring fills, ( I need to move it a tad left for target and right for player) I’ll see if I can make the power ring class colored on my own.

And if possible, for the future seeing if we can make the animation run more smoothly would be awesome.