ArcHUD3

ArcHUD3

594k Downloads

Suggestion: Legion Rune Bar Sorting #97

nyyr opened this issue ยท 1 comments

commented

Garrett128 created this issue Sep 14, 2017

Hi,

Having some free time and also having recently re-started playing an old Death Knight alt, i started to think about and implement a DK rune bar overhaul. Given that we now have Just one rune type, i have put them in a sorted order just like in the legion default interface. Then i dimmed incomplete runes so as to have a more visually relevant idea of how many runes we have. You then combine this with the combo points runes representation it works pretty well.

You can find my code of the rune bar below :

local module = ArcHUD:NewModule("Runes")
local _, _, rev = string.find("$Rev: 138 $", "([0-9]+)")
module.version = "3.0 (r" .. rev .. ")"

module.unit = "player"
module.noAutoAlpha = nil

local shadingFactor = 0.4

module.defaults = {
profile = {
Enabled = true,
Outline = true,
Flash = true,
Side = 2,
Level = 1,
ShowSeparators = true,
SortRunes = true,
Color = {r = 0, g = 0.6, b = 0.7},
PartialColor = {r = 0 * shadingFactor, g = 0.6 * shadingFactor, b = 0.7 * shadingFactor},
-- PartialColor = {r = 0, g = 0.5, b = 0.6},
-- PartialColor = {r = 0, g = 1, b = 0},
RingVisibility = 2, -- always fade out when out of combat, regardless of ring status
}
}
module.options = {
attach = true,
hasseparators = true,
{name = "SortRunes", text = "SORTRUNES", tooltip = "SORTRUNES"},
}
module.localized = true

local MAX_RUNES = 6
local MAX_RING_VALUE = 100

local RuneLastState = {
[1] = true,
[2] = true,
[3] = true,
[4] = true,
[5] = true,
[6] = true,
}

function module:Initialize()
-- Setup the frame we need
self.f = self:CreateRing(true, ArcHUDFrame)
self.f:SetAlpha(0)

self:CreateStandardModuleOptions(55)

end

function module:OnModuleUpdate()

local _, class = UnitClass(self.unit)
if class ~= "DEATHKNIGHT" then return end

self:UpdateRunes()	

if self.db.profile.SortRunes then
	self:UpdateRuneCooldown(arg1, arg2)
end

end

function module:OnModuleEnable()

local _, class = UnitClass(self.unit)
if class ~= "DEATHKNIGHT" then return end

if (not self.frames) then
	-- create frame for each rune
	self.frames = {}
	self.frames[1] = self.f
	for i=2,6 do
		self.frames[i] = self:CreateRing(false, ArcHUDFrame)
		self.frames[i]:SetAlpha(0)
	end
	
	-- set angles
	local angles = {
		[1] = { e = 180, s = 135 },
		[2] = { e = 135, s = 112 },
		[3] = { e = 112, s = 90 },
	}
	for i=4,6 do
		angles[i] = {
			e = 180 - angles[3-math.fmod(i-1,3)].s,
			s = 180 - angles[3-math.fmod(i-1,3)].e,
		}
	end
	
	-- configure rune arcs
	for i=1,6 do
		self:AttachRing(self.frames[i])
		if i <= 1 then
			-- self.frames[i].inverseFill = true
		end
		self.frames[i].linearFade = true
		self.frames[i]:SetEndAngle(angles[i].e)
		self.frames[i]:SetStartAngle(angles[i].s)
		self.frames[i]:SetShineAngle((angles[i].e + angles[i].s)/2)
		self.frames[i]:SetMax(MAX_RING_VALUE)
		self.frames[i]:SetValue(MAX_RING_VALUE, 0)
		if (i > 1) then
			self.frames[i].sparkRed:SetVertexColor(0.5, 0.5, 0.5)
			self.frames[i]:SetSpark(99.9, true)
		end
		self.frames[i].dirty = true
		self.frames[i].isRune = true
		--self:Debug(1, "Rune %d set up (angle %f-%f)", i, angles[i].s, angles[i].e)
	end
	
	-- adjust shine for first and last rune
	self.frames[1]:SetShineAngle(angles[1].s + 12)
	self.frames[6]:SetShineAngle(angles[6].e - 12)
end

-- un-register all previous events
self:UnregisterAllEvents();

-- Register the events we will use
	
-- Runes
self:RegisterEvent("RUNE_POWER_UPDATE", "UpdatePower")
self:RegisterEvent("RUNE_TYPE_UPDATE", "UpdatePower")

-- Unit Power
self:RegisterUnitEvent("UNIT_POWER", "UpdatePower", self.unit)
self:RegisterUnitEvent("UNIT_POWER_FREQUENT", "UpdatePower", self.unit)

-- Entering/Leaving Combat
self:RegisterEvent("PLAYER_REGEN_ENABLED", "UpdatePower")
self:RegisterEvent("PLAYER_REGEN_DISABLED", "UpdatePower")

-- Dying/Ressurecting/Insance Zoning
self:RegisterEvent("PLAYER_ENTERING_WORLD", "UpdatePower")
self:RegisterEvent("PLAYER_DEAD", "UpdatePower")
self:RegisterEvent("PLAYER_UNGHOST", "UpdatePower")
self:RegisterEvent("PLAYER_ALIVE", "UpdatePower")
	
-- self:UpdateRunes()

-- Activate ring timers
self:StartRingTimers()

end

function module:UpdateRunes()
-- update all runes
if (self.frames) then
for i=1,MAX_RUNES do
local start, duration, runeReady = GetRuneCooldown(i)
if not runeReady and start then
self.frames[i]:UpdateColor(self.db.profile.PartialColor)
else
self.frames[i]:UpdateColor(self.db.profile.Color)
end
end
end
end

function module:GetRuneOrder()

local RemainingTime = {}
local RuneOrder = {}
local issorted = false
local unsortedsize = MAX_RUNES-1
	
for i = 1, MAX_RUNES do
	local start, duration, runeReady = GetRuneCooldown(i)
	if duration and start then 
		RemainingTime[i] = duration - start
		RuneOrder [i] = i
	end	
end

if RuneOrder == {} or RemainingTime == {} then 
	return 0
end	

while not issorted and unsortedsize ~= 0 do 
	issorted = true
	for i = 1, unsortedsize do 
		if RemainingTime[i] > RemainingTime[i+1] then 
			local tempval = RemainingTime[i];
			RemainingTime[i] = RemainingTime[i+1]
			RemainingTime[i+1] = tempval
			
			local tempval = RuneOrder[i];
			RuneOrder[i] = RuneOrder[i+1]
			RuneOrder[i+1] = tempval
			
			issorted = false
		end	
	end
	
	unsortedsize = unsortedsize - 1
	
end 

return (RuneOrder or 0)

end

function module:UpdateSortedRuneCooldown(runeIndex)

for i=1,MAX_RUNES do

	local RuneOrder = self:GetRuneOrder()
	runeIndex = RuneOrder[i]
	
	if runeIndex then
		local start, duration, runeReady = GetRuneCooldown(runeIndex)
		
		if not runeReady then
			if start then
				self.frames[i]:UpdateColor(self.db.profile.PartialColor)
				self.frames[i]:SetValue(MAX_RING_VALUE, duration, start, 0)
				RuneLastState[i] = false
			end
		else
			self.frames[i]:UpdateColor(self.db.profile.Color)
			self.frames[i]:SetValue(MAX_RING_VALUE, 0)
			if RuneLastState[i] == false then 
				self.frames[i]:DoShine()
			end
			RuneLastState[i] = true
		end
	end
end

end

function module:UpdateUnsortedRuneCooldown(runeIndex)

if not runeIndex then 
	return
end

local start, duration, runeReady = GetRuneCooldown(runeIndex)

--self:Debug(1, "R %s, S %s, D %s, RR %s", tostring(runeIndex), tostring(start), tostring(duration), tostring(runeReady))

if not runeReady then
	if start then
		self.frames[runeIndex]:UpdateColor(self.db.profile.PartialColor)
		self.frames[runeIndex]:SetValue(MAX_RING_VALUE, duration, start, 0)
		RuneLastState[i] = false
	end
else
	self.frames[runeIndex]:UpdateColor(self.db.profile.Color)
	self.frames[runeIndex]:SetValue(MAX_RING_VALUE, 0)
	if RuneLastState[i] == false then
		self.frames[runeIndex]:DoShine()
	end	
	RuneLastState[i] = true
end

end

function module:UpdateRuneCooldown(runeIndex, isEnergize)

if self.db.profile.SortRunes then
	-- update and sort all runes
	self:UpdateSortedRuneCooldown()
else
	if not runeIndex then
		-- update all runes
		for i=1,MAX_RUNES do
			self:UpdateUnsortedRuneCooldown(i)
		end
	else
		-- just update the current rune
		self:UpdateUnsortedRuneCooldown(runeIndex)
	end	
end

end

function module:UpdatePower(event, arg1, arg2)
if event == "PLAYER_ENTERING_WORLD" then
self:UpdateRunes()
self:UpdateRuneCooldown(arg1, arg2)
elseif event == "RUNE_POWER_UPDATE" or event == "RUNE_TYPE_UPDATE" then
self:UpdateRuneCooldown(arg1, arg2)
else
self:UpdateRuneCooldown(arg1, arg2)
end
end

And also the modifications i did to the combo points counter for the death knight, to count available runes :

--
-- Show combo points / holy power / soul shards

local _
local class = ""

function ArcHUD:InitComboPointsFrame()

-- NOTE: Get Player's Class
_, class = UnitClass("player")

-- NOTE: Demon Hunter Events
if class == "DEMONHUNTER" then 
	self:RegisterEvent("UNIT_AURA", "UpdateComboPoints");
end	

-- NOTE: Deathknight Events
if class == "DEATHKNIGHT" then 
	self:RegisterEvent("RUNE_POWER_UPDATE", "UpdateComboPoints");
	self:RegisterEvent("RUNE_TYPE_UPDATE", "UpdateComboPoints");
end

-- NOTE: General Events

-- NOTE: Unit Power
self:RegisterEvent("UNIT_POWER", "UpdateComboPoints")
self:RegisterEvent("UNIT_POWER_FREQUENT", "UpdateComboPoints")

-- NOTE: Entering/Leaving Combat
self:RegisterEvent("PLAYER_REGEN_ENABLED", "UpdateComboPoints")
-- self:RegisterEvent("PLAYER_REGEN_DISABLED", "UpdateComboPoints")

-- NOTE: Dying/Ressurecting/Insance Zoning
self:RegisterEvent("PLAYER_ENTERING_WORLD", "UpdateComboPoints")
self:RegisterEvent("PLAYER_DEAD", "UpdateComboPoints")
self:RegisterEvent("PLAYER_UNGHOST", "UpdateComboPoints")
self:RegisterEvent("PLAYER_ALIVE", "UpdateComboPoints")

self.TargetHUD.Combo:SetTextColor(self.db.profile.ColorComboPoints.r, self.db.profile.ColorComboPoints.g, self.db.profile.ColorComboPoints.b)

-- NOTE: Show/Hide combopoints display
if(self.db.profile.ShowComboPoints) then
	self.TargetHUD.Combo:Show()
else
	self.TargetHUD.Combo:Hide()
end

end

function ArcHUD:SetComboPoints(points)
if (points > 0) then
self.TargetHUD.Combo:SetText(points)
else
self.TargetHUD.Combo:SetText("")
end
end

function ArcHUD:UpdateComboPointsFrame()

local points = 0
local _, class = UnitClass("player")

if (class == "PALADIN") then
	points = UnitPower("player", SPELL_POWER_HOLY_POWER)
elseif (class == "WARLOCK") then
	points = UnitPower("player", SPELL_POWER_SOUL_SHARDS)
elseif (class == "MONK") then
	points = UnitPower("player", SPELL_POWER_CHI)
elseif (class == "DEATHKNIGHT") then
	points = self:get_active_runes()
elseif (class == "DEMONHUNTER") then		
	local _, _, _, count = UnitAura("player", "Soul Fragments")
	if count then 
		points = count
	else 
		points = 0
	end
elseif (class == "ROGUE" or class == "DRUID") then
	local powerType, powerToken = UnitPowerType("player");
	if (powerType == SPELL_POWER_ENERGY) then
		points = UnitPower("player", SPELL_POWER_COMBO_POINTS);
	end
end
self:SetComboPoints(points)

end

function ArcHUD:get_active_runes()
local runecount = 0;
for runeslot=1,6 do
local _, _, runeready = GetRuneCooldown(runeslot);
if (runeready) then
runecount = runecount + 1;
end
end
return runecount
end

function ArcHUD:UpdateComboPoints(event, arg1, arg2)
self:UpdateComboPointsFrame()
end

Along with the code for the Death Knight runes there is also code to add the Demon Hunter's Soul Fragments to the combo points display

Cheers,

Garrett

commented

integrated & tested for next release, thanks!