Rework the way triggers and indicators work

Triggers are now a link between link between a datasource (for now only
AuraList) and an indicator (for now only SquareIndicator).
This commit is contained in:
2023-03-19 06:54:02 +01:00
parent 5d21f1030c
commit 825738a040
4 changed files with 152 additions and 80 deletions

View File

@@ -18,6 +18,7 @@ local omi = select(2, ...)
local types = omi.GetModule("types") local types = omi.GetModule("types")
local AuraTrigger = types.AuraTrigger local AuraTrigger = types.AuraTrigger
--- AuraList keeps track of all the auras attached to a unitframe
local AuraList = types.CreateClass("AuraList") local AuraList = types.CreateClass("AuraList")
types.AuraList = AuraList types.AuraList = AuraList
@@ -38,6 +39,8 @@ local statusLists = {
} }
} }
-- helper function that goes over several aura slots for a unit. Runs a
-- callback function with UnitAuraInfo table as argument
local function ForEachAuraSlots(unit, fn, continuationToken, ...) local function ForEachAuraSlots(unit, fn, continuationToken, ...)
local GetAuraDataBySlot = C_UnitAuras.GetAuraDataBySlot local GetAuraDataBySlot = C_UnitAuras.GetAuraDataBySlot
local n = select('#', ...) local n = select('#', ...)
@@ -48,6 +51,7 @@ local function ForEachAuraSlots(unit, fn, continuationToken, ...)
return continuationToken return continuationToken
end end
-- Helper function that goes over all aura slots for a given filter
local function ForEachAuraFiltered(unit, filter, fn) local function ForEachAuraFiltered(unit, filter, fn)
local UnitAuraSlots = UnitAuraSlots local UnitAuraSlots = UnitAuraSlots
local continuationToken = nil local continuationToken = nil
@@ -63,6 +67,9 @@ local function ForEachAura(unit, fn)
ForEachAuraFiltered(unit, "HARMFUL", fn) ForEachAuraFiltered(unit, "HARMFUL", fn)
end end
--- Initialize AuraList
-- unitframe
-- The unitframe this list belongs to
function AuraList:Init(unitframe) function AuraList:Init(unitframe)
self.unitframe = unitframe self.unitframe = unitframe
self.unit = unitframe.unit self.unit = unitframe.unit
@@ -75,20 +82,20 @@ function AuraList:Init(unitframe)
self.triggersBySpell = {} self.triggersBySpell = {}
end end
--- Add an AuraTrigger to this AuraList
-- trigger
-- an AuraTrigger object
function AuraList:AddTrigger(trigger) function AuraList:AddTrigger(trigger)
table.insert(self.triggers, trigger) table.insert(self.triggers, trigger)
if trigger:IsInstanceOf(AuraTrigger) then
local spellId = trigger.spellId local spellId = trigger.spellId
if self.triggersBySpell[spellId] == nil then if self.triggersBySpell[spellId] == nil then
self.triggersBySpell[spellId] = {} self.triggersBySpell[spellId] = {}
end end
table.insert(self.triggersBySpell[spellId], trigger) table.insert(self.triggersBySpell[spellId], trigger)
end
end end
-- Scan all the auras of the owning unit and build the aura list from scratch --- Reset the AuraList and do a full aura scan on the unit
-- This should only ever happen when the unit is first assigned to the frame -- This also resets all the triggers that are attached to this AuraList
function AuraList:Reset() function AuraList:Reset()
self.auras = {} self.auras = {}
@@ -103,6 +110,9 @@ function AuraList:Reset()
end) end)
end end
--- Add an aura to the AuraList
-- aura
-- A UnitAuraInfo table for a newly added aura
function AuraList:AddAura(aura) function AuraList:AddAura(aura)
local statusChanged = false local statusChanged = false
self.auras[aura.auraInstanceID] = aura self.auras[aura.auraInstanceID] = aura
@@ -126,7 +136,9 @@ function AuraList:AddAura(aura)
return statusChanged return statusChanged
end end
-- Returns a table with every status that applies to the given aura --- Return a sequence table with all status names that match the given aura
-- aura
-- A UnitAuraInfo table
function AuraList:GetStatusForAura(aura) function AuraList:GetStatusForAura(aura)
local status = {} local status = {}
if aura.dispelName and aura.isHarmful then if aura.dispelName and aura.isHarmful then
@@ -140,6 +152,9 @@ function AuraList:GetStatusForAura(aura)
return status return status
end end
--- Add an aura to the AuraList
-- aura
-- A UnitAuraInfo table for a newly added aura
function AuraList:UpdateAura(aura) function AuraList:UpdateAura(aura)
for _, trigger in ipairs(self.triggersBySpell[aura.spellId] or {}) do for _, trigger in ipairs(self.triggersBySpell[aura.spellId] or {}) do
trigger:UpdateAura(self.auras[aura.auraInstanceId], aura) trigger:UpdateAura(self.auras[aura.auraInstanceId], aura)
@@ -147,6 +162,9 @@ function AuraList:UpdateAura(aura)
self.auras[aura.auraInstanceID] = aura self.auras[aura.auraInstanceID] = aura
end end
--- Remove an aura from the AuraList
-- iid
-- The instance id for a removed aura
function AuraList:RemoveAura(iid) function AuraList:RemoveAura(iid)
local aura = self.auras[iid] local aura = self.auras[iid]
if aura == nil then if aura == nil then
@@ -174,6 +192,9 @@ function AuraList:RemoveAura(iid)
return statusChanged return statusChanged
end end
--- Update an aura in the AuraList
-- info
-- The new UnitAuraInfo structure
function AuraList:Update(info) function AuraList:Update(info)
local GetAuraDataByAuraInstanceID = C_UnitAuras.GetAuraDataByAuraInstanceID local GetAuraDataByAuraInstanceID = C_UnitAuras.GetAuraDataByAuraInstanceID
local statusChanged = false local statusChanged = false

View File

@@ -18,13 +18,35 @@ local omi = select(2, ...)
local types = omi.GetModule("types") local types = omi.GetModule("types")
local AuraTrigger = types.AuraTrigger local AuraTrigger = types.AuraTrigger
--- Indicator is a type for any kind of visual indicator. This type has no
-- function and should not be created. All functional indicators derive from
-- this type.
local Indicator = types.CreateClass("Indicator") local Indicator = types.CreateClass("Indicator")
types.Indicator = Indicator types.Indicator = Indicator
-- TODO: make different indicators, such as texture, text, border --- Show the indicator and supply it with data
-- TODO: spellid feels out of place but I wanna quickly get something for now. function Indicator:Show(data)
-- Should rethink this when Trigger is its own class end
function Indicator:Init(unitframe, auralist, size, point, x, y, color, spellId)
--- Update the indicator with new data
function Indicator:Update(data)
end
--- Hide the indicator
function Indicator:Hide()
end
--- SquareIndicator is an indicator that displays a colored square texture
local SquareIndicator = types.CreateClass("SquareIndicator", Indicator)
types.SquareIndicator = SquareIndicator
--- Initialize a new SquareIndicator
-- unitframe Unitframe it is attached to
-- size Size of the indicator square
-- point Attachment point to the unitframe's overlay frame
-- x, y x and y offset for the attachment point
-- color Color of the square
function SquareIndicator:Init(unitframe, size, point, x, y, color)
local frame = unitframe.overlays local frame = unitframe.overlays
local texture = frame:CreateTexture(nil, "ARTWORK") local texture = frame:CreateTexture(nil, "ARTWORK")
@@ -36,26 +58,21 @@ function Indicator:Init(unitframe, auralist, size, point, x, y, color, spellId)
self:SetColor(color) self:SetColor(color)
texture:SetVertexColor(unpack(color)) texture:SetVertexColor(unpack(color))
texture:SetDrawLayer("ARTWORK") texture:SetDrawLayer("ARTWORK")
local fn = function(activate)
if activate then
self:Show()
else
self:Hide()
end
end
local trigger = AuraTrigger:new(fn, spellId, true)
auralist:AddTrigger(trigger)
end end
function Indicator:SetColor(color) --- Set the color of the Square Indicator
-- color: a sequence table with 3 color channels {r, g, b}
function SquareIndicator:SetColor(color)
self.texture:SetVertexColor(unpack(color)) self.texture:SetVertexColor(unpack(color))
end end
function Indicator:Show() --- Show the square indicator.
-- data: ignored for now
function SquareIndicator:Show(data)
self.texture:Show() self.texture:Show()
end end
function Indicator:Hide() --- Hide the square indicator.
function SquareIndicator:Hide()
self.texture:Hide() self.texture:Hide()
end end

View File

@@ -17,47 +17,52 @@
local omi = select(2, ...) local omi = select(2, ...)
local types = omi.GetModule("types") local types = omi.GetModule("types")
---
-- Trigger objects provide a link between indicators and some data source. They
-- can pass data from the data source into the trigger. This is just a
-- supertype for all indicators and should not be constructed on its own. It
-- has no functionality other than providing default implementations that do
-- nothing.
types.Trigger = types.CreateClass("Trigger") types.Trigger = types.CreateClass("Trigger")
local Trigger = types.Trigger local Trigger = types.Trigger
-- Constructor for Trigger. -- Initialize a new Trigger object
-- fn: -- indicator:
-- The callback function that gets called every time this trigger changes -- The indicator that gets activated by this trigger.
-- states between active and inactive. Function takes one boolean argument. function Trigger:Init(indicator)
function Trigger:Init(fn) self.indicator = indicator
self.fn = fn
end end
-- Returns whether the trigger is active -- Returns whether or not the trigger is active
function Trigger:IsActive() function Trigger:IsActive()
return false return false
end end
-- Reset the trigger to the default state. Does run the untrigger callback if -- Reset the trigger to the default state. Deactivates the indicator if it is
-- the trigger is active when Reset is called. -- active when Reset is called.
function Trigger:Reset() function Trigger:Reset()
end end
---
-- AuraTrigger is a trigger that can be attached to AuraList as datasource
types.AuraTrigger = types.CreateClass("AuraTrigger", Trigger) types.AuraTrigger = types.CreateClass("AuraTrigger", Trigger)
local AuraTrigger = types.AuraTrigger local AuraTrigger = types.AuraTrigger
-- Constructor for AuraTrigger --- Initialize a new AuraTrigger object
-- fn: -- indicator
-- The callback function that gets called every time this trigger changes -- Indicator that gets controlled by this trigger.
-- states between active and inactive. Function takes one boolean argument. -- spellId
-- -- Spell id to trigger on
-- requiredCount = 1: -- own
-- The minimum number of conditions that must be met before the trigger -- Only trigger on auras by the player
-- activates. What exactly this means depends on the trigger, examples are -- requiredCount=1
-- # of matching auras, # of stacks. -- Number of aura applications to activate trigger
-- -- invert=false
-- invert = false: -- Whether to invert trigger activation
-- If the trigger is inverted it will activate when count < requiredCount. function AuraTrigger:Init(indicator, spellId, own, requiredCount, invert)
-- If the trigger is not inverted it will activate when count >= requiredCount. Trigger.Init(self, indicator)
function AuraTrigger:Init(fn, spellId, own, requiredCount, invert)
Trigger.Init(self, fn)
self.spellId = spellId self.spellId = spellId
self.requiredCount = requiredCount or 1 self.requiredCount = requiredCount or 1
self.own = own self.own = own
@@ -65,18 +70,19 @@ function AuraTrigger:Init(fn, spellId, own, requiredCount, invert)
self.invert = invert or false self.invert = invert or false
end end
-- Reset the trigger to the default state. Does run the untrigger callback if --- See Trigger:Reset
-- the trigger is active when it is called
function AuraTrigger:Reset() function AuraTrigger:Reset()
local before = self:IsActive() local before = self:IsActive()
self.count = 0 self.count = 0
local after = self:IsActive() local after = self:IsActive()
if before ~= after then if not before and after then
self.fn(after) self.indicator:Show()
elseif before and not after then
self.indicator:Hide()
end end
end end
-- Return true if the aura matches the trigger --- Return true if the aura matches the trigger
-- aura: -- aura:
-- Must be a valid UnitAuraInfo structure. -- Must be a valid UnitAuraInfo structure.
function AuraTrigger:IsMatching(aura) function AuraTrigger:IsMatching(aura)
@@ -89,7 +95,7 @@ function AuraTrigger:IsMatching(aura)
return true return true
end end
-- Inform the trigger about an added aura --- Inform the trigger about an added aura
-- aura: -- aura:
-- Must be a valid UnitAuraInfo -- Must be a valid UnitAuraInfo
function AuraTrigger:AddAura(aura) function AuraTrigger:AddAura(aura)
@@ -100,11 +106,15 @@ function AuraTrigger:AddAura(aura)
-- Be mindful, this works only if count always changes by 1. -- Be mindful, this works only if count always changes by 1.
if self.count == self.requiredCount then if self.count == self.requiredCount then
self.fn(not self.invert) if self.invert then
self.indicator:Hide()
else
self.indicator:Show()
end
end end
end end
-- Inform the trigger about an updated aura --- Inform the trigger about an updated aura
-- before: -- before:
-- Must be a valid UnitAuraInfo for the aura before the update -- Must be a valid UnitAuraInfo for the aura before the update
-- after: -- after:
@@ -113,7 +123,8 @@ function AuraTrigger:UpdateAura(before, after)
-- --
end end
-- Inform the trigger about an aura that got removed --- Inform the trigger about an aura that got removed
-- aura:
-- Must be a valid UnitAuraInfo for the aura before it got removed -- Must be a valid UnitAuraInfo for the aura before it got removed
function AuraTrigger:RemoveAura(aura) function AuraTrigger:RemoveAura(aura)
if not self:IsMatching(aura) then if not self:IsMatching(aura) then
@@ -122,11 +133,15 @@ function AuraTrigger:RemoveAura(aura)
self.count = self.count - 1 self.count = self.count - 1
-- Be mindful, this works only if count always changes by 1. -- Be mindful, this works only if count always changes by 1.
if self.count == self.requiredCount - 1 then if self.count == self.requiredCount - 1 then
self.fn(self.invert) if self.invert then
self.indicator:Show()
else
self.indicator:Hide()
end
end end
end end
-- Returns true if the trigger is active, false otherwise --- Returns true if the trigger is active, false otherwise
function AuraTrigger:IsActive() function AuraTrigger:IsActive()
if self.invert then if self.invert then
return self.count < self.requiredCount return self.count < self.requiredCount

View File

@@ -18,7 +18,8 @@ local omif = select(2, ...)
local types = omif.GetModule("types") local types = omif.GetModule("types")
local StatusBar = types.StatusBar local StatusBar = types.StatusBar
local AuraList = types.AuraList local AuraList = types.AuraList
local Indicator = types.Indicator local SquareIndicator = types.SquareIndicator
local AuraTrigger = types.AuraTrigger
types.UnitFrame = types.CreateClass("UnitFrame") types.UnitFrame = types.CreateClass("UnitFrame")
local UnitFrame = types.UnitFrame local UnitFrame = types.UnitFrame
@@ -59,11 +60,29 @@ function UnitFrame:Init(unit, width, height, hideInRaid)
overlays:Show() overlays:Show()
self.overlays = overlays self.overlays = overlays
self:CreateName() self:CreateName()
self.indicators = {
Indicator:new(self, self.auras, 14, "TOPLEFT", 2, -2, colors.white, 383648), self.auras:AddTrigger(
Indicator:new(self, self.auras, 14, "TOPLEFT", 2, -2, colors.white, 974), AuraTrigger:new(
Indicator:new(self, self.auras, 14, "BOTTOMLEFT", 2, 2, colors.cyan, 61295) SquareIndicator:new(self, 14, "TOPLEFT", 2, -2, colors.white),
} 383648, -- Second Earthshield on self
true
)
)
self.auras:AddTrigger(
AuraTrigger:new(
SquareIndicator:new(self, 14, "TOPLEFT", 2, -2, colors.white),
974, -- Earthshield
true
)
)
self.auras:AddTrigger(
AuraTrigger:new(
SquareIndicator:new(self, 14, "BOTTOMLEFT", 2, 2, colors.cyan),
61295, -- Riptide
true
)
)
self:RegisterEvents() self:RegisterEvents()
self:Enable() self:Enable()
end end