vrtc / chorus (public) (License: CC0) (since 2023-08-12) (hash sha1)
World of Warcraft add-on stub. The overall goal is to create a specialized raid frame.

/src/ChorusAuraFrameTemplate.lua (dcd812187c5c003e5e8f0355cd94c1540034dd9f) (6242 bytes) (mode 100644) (type blob)

local Chorus = Chorus

local auraButtonEventProcessor = Chorus.auraButtonEventProcessor

local UnitAura = Chorus.test.UnitAura or UnitAura
local UnitExists = Chorus.test.UnitExists or UnitExists
local UnitIsConnected = Chorus.test.UnitIsConnected or UnitIsConnected

local SecureButton_GetUnit = Chorus.test.SecureButton_GetUnit or SecureButton_GetUnit

--[[ See `FrameXML/BuffFrame.lua:BUFF_MAX_DISPLAY`. ]]--
local BUFF_MAX_DISPLAY = BUFF_MAX_DISPLAY or 36
--[[ See `FrameXML/BuffFrame.lua:DEBUFF_MAX_DISPLAY`. ]]--
local DEBUFF_MAX_DISPLAY = DEBUFF_MAX_DISPLAY or 36

--[[ TODO Globalize special spells. ]]--
local auraWeightMap = {
	['Aimed Shot'] = 1,
	['Anti-Magic Shell'] = 2,
	['Avenging Wrath'] = 3,
	['Bladestorm'] = 3,
	['Blind'] = 3,
	['Chains of Ice'] = 1,
	['Cheap Shot'] = 1,
	['Concussion Blow'] = 1,
	['Cyclone'] = 3,
	['Deterrence'] = 3,
	['Disarm'] = 2,
	['Disarmed'] = 2,
	['Divine Plea'] = 1,
	['Divine Shield'] = 3,
	['Entangling Roots'] = 1,
	['Evasion'] = 2,
	['Evocation'] = 2,
	['Fear'] = 2,
	['Freezing Trap'] = 2,
	['Gnaw'] = 2,
	['Gouge'] = 2,
	['Grounding Totem Effect'] = 2,
	['Grounding Totem'] = 2,
	['Hammer of Justice'] = 1,
	['Hamstring'] = 1,
	['Hand of Freedom'] = 1,
	['Hand of Protection'] = 2,
	['Hand of Sacrifice'] = 1,
	['Hand of Salvation'] = 1,
	['Hex'] = 2,
	['Hibernation'] = 2,
	['Ice Block'] = 3,
	['Icebound Fortitude'] = 3,
	['Kidney Shot'] = 1,
	['Last Stand'] = 3,
	['Mind Control'] = 3,
	['Mortal Strike'] = 1,
	['Polymorph'] = 2,
	['Psychic Scream'] = 2,
	['Recklessness'] = 3,
	['Sacrifice'] = 2,
	['Sap'] = 3,
	['Seduction'] = 2,
	['Shield Block'] = 2,
	['Shield Wall'] = 3,
	['Sleep'] = 2,
	['Spell Reflection'] = 2,
	['Unstable Affliction'] = 2,
	['Wounding Poison'] = 1,
}

Chorus.auraWeightMap = auraWeightMap

local function getAuraWeight(unitDesignation, auraIndex, filter)
	local name, _, _, _, _, durationSec, owner = UnitAura(unitDesignation, auraIndex, filter)
	if not name then
		return 0
	end

	local specialPrio = auraWeightMap[name] or 0
	specialPrio = math.min(math.max(1, math.floor(math.abs(specialPrio))), 9)

	local durationIsLimited = 1
	if not durationSec or durationSec < 1 then
		durationIsLimited = 0
	end

	local ownerPrio = 1
	if 'player' == owner then
		ownerPrio = 2
	end

	local weight = 100000 * ownerPrio +
	               10000 * specialPrio +
		       durationIsLimited * (7201 - math.min(durationSec, 7200))
	return math.abs(math.floor(weight))
end

local function getAuraPriorityList(unitDesignation, filter)
	local t = {}
	local i = 0
	while (i < 8192) do
		i = i + 1
		local name = UnitAura(unitDesignation, i, filter)
		if not name then
			break
		end
		t[i] = i
	end

	table.sort(t, function(a, b)
		local p = getAuraWeight(unitDesignation, a, filter)
		local q = getAuraWeight(unitDesignation, b, filter)
		return p > q
	end)

	return t
end

local function auraFrameEventProcessor(self, eventCategory, ...)
	assert(self ~= nil)

	--[[ Optimization hack ]]--
	--[[ Make it use table value if it lags again. ]]--
	local unitDesignation = SecureButton_GetUnit(self) or 'none'
	assert(unitDesignation ~= nil)
	assert('string' == type(unitDesignation))
	unitDesignation = string.lower(strtrim(unitDesignation))
	assert(string.len(unitDesignation) >= 1)
	assert(string.len(unitDesignation) <= 256)

	if UnitExists(unitDesignation) and UnitIsConnected(unitDesignation) then
		self:Show()
	else
		self:Hide()
		return
	end

	if 'UNIT_AURA' == eventCategory then
		local u = select(1, ...)
		if u and 'string' == type(u) and unitDesignation ~= u then
			return
		end
	end

	--[[ Make it use table value if it lags again. ]]--
	local filter = SecureButton_GetAttribute(self, 'filter')
	assert(filter ~= nil)
	assert('string' == type(filter))
	filter = string.upper(strtrim(filter))
	assert(string.len(filter) >= 1)
	assert(string.len(filter) <= 256)

	local q = getAuraPriorityList(unitDesignation, filter)
	assert(q ~= nil)
	assert('table' == type(q))

	local i = 0
	local t = {self:GetChildren()}
	while (i < #q) do
		i = i + 1
		local b = t[i]
		if not b then
			break
		end
		assert(b ~= nil)

		local k = q[i]
		assert(k ~= nil)
		assert('number' == type(k))
		k = math.floor(math.abs(k))
		assert(k >= 0)
		b.index = k

		auraButtonEventProcessor(b, eventCategory, ...)
	end

	while (i < #t) do
		i = i + 1
		local b = t[i]
		assert(b ~= nil)
		b.index = 0

		auraButtonEventProcessor(b, eventCategory, ...)
	end
end

local function createEveryAuraButton(auraFrame)
	local self = auraFrame
	assert(self ~= nil)

	local n = self:GetName()
	assert(n ~= nil)

	local max = math.max(BUFF_MAX_DISPLAY or 36, DEBUFF_MAX_DISPLAY or 36) or 36
	local i = 0
	local w = math.max(self:GetWidth(), 30)
	local bmaxh = 30
	local x = 0
	local y = 0
	while (i < max) do
		i = i + 1

		local m = string.format('%sAuraButton%02d', n, i)
		local b = CreateFrame('FRAME', m, self, 'ChorusAuraButtonTemplate')
		b:SetPoint('TOPLEFT', x, -y)
		x = x + b:GetWidth()
		bmaxh = math.max(bmaxh, b:GetHeight())
		if x > w then
			x = 0
			y = y + bmaxh
		end
	end
end

function Chorus.auraFrameMain(self)
	assert(self ~= nil)

	--[[ `main` function is called on frame load. Frames declared in the
	XML descriptor are allocated before that. It is preferrable to use
	the XML descriptor for performance and separation of concerns. When
	no descendant frames were declared in this manner or another,
	fallback to allocating required aura buttons dynamically. ]]--

	local createAuraButtonsDynamically = nil == self:GetChildren()
	if createAuraButtonsDynamically then
		createEveryAuraButton(self)
	end

	self:RegisterEvent('PARTY_MEMBERS_CHANGED')
	self:RegisterEvent('PLAYER_FOCUS_CHANGED')
	self:RegisterEvent('PLAYER_LOGIN')
	self:RegisterEvent('PLAYER_TARGET_CHANGED')
	self:RegisterEvent('UNIT_AURA')

	--[[ Disable redundant aura updates. ]]--
	local t = {self:GetChildren()}
	local i = 0
	while (i < #t) do
		i = i + 1
		local b = t[i]
		if b then
			b:UnregisterEvent('UNIT_AURA')
		end
	end

	--[[ Optimization hack. This function call is expensive. Probably. ]]--
	self.unit = SecureButton_GetUnit(self)
	self.filter = SecureButton_GetAttribute(self, 'filter')

	self:SetScript('OnEvent', auraFrameEventProcessor)
	self:SetScript('OnShow', auraFrameEventProcessor)
end


Mode Type Size Ref File
100644 blob 35 5c40e6e2862d70b5c51c326a13073a4012ac05c7 .gitignore
100644 blob 1095 ea691376a4847fd792cb2ae4b1ffebbd6bd6e22a Makefile
100644 blob 3394 190d35463349336b8ed9d90078342c5643600a29 README.adoc
100644 blob 234 fca3e2be1b6aacf0e93e898255a4049a33b35d36 chorus.toc
040000 tree - cac8af7ca5d8adc7771598e6faae482998b25fa2 doc
040000 tree - b232717e4ce1bb949a6e4e68fbf679b026b48fd0 etc
040000 tree - c04e1001c2dd401037b01ddd04fdeccc99be2ec3 share
040000 tree - ebdeb4d98909af86d2423f9845ed706466beccbc src
040000 tree - aa42c74ca307c2c3c635efd4186be6e5404a6303 test
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/vrtc/chorus

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/vrtc/chorus

Clone this repository using git:
git clone git://git.rocketgit.com/user/vrtc/chorus

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main