diff --git a/ElvUI/Libraries/oUF/elements/classpower.lua b/ElvUI/Libraries/oUF/elements/classpower.lua new file mode 100644 index 0000000..f711de8 --- /dev/null +++ b/ElvUI/Libraries/oUF/elements/classpower.lua @@ -0,0 +1,313 @@ +--[[ +# Element: ClassPower + +Handles the visibility and updating of the player's class resources (like Chi Orbs or Holy Power) and combo points. + +## Widget + +ClassPower - An `table` consisting of as many StatusBars as the theoretical maximum return of [UnitPowerMax](http://wowprogramming.com/docs/api/UnitPowerMax.html). + +## Sub-Widgets + +.bg - A `Texture` used as a background. It will inherit the color of the main StatusBar. + +## Sub-Widget Options + +.multiplier - Used to tint the background based on the widget's R, G and B values. Defaults to 1 (number)[0-1] + +## Notes + +A default texture will be applied if the sub-widgets are StatusBars and don't have a texture set. +If the sub-widgets are StatusBars, their minimum and maximum values will be set to 0 and 1 respectively. + +Supported class powers: + - All - Combo Points + - Mage - Arcane Charges + - Monk - Chi Orbs + - Paladin - Holy Power + - Warlock - Soul Shards + +## Examples + + local ClassPower = {} + for index = 1, 10 do + local Bar = CreateFrame('StatusBar', nil, self) + + -- Position and size. + Bar:SetSize(16, 16) + Bar:SetPoint('TOPLEFT', self, 'BOTTOMLEFT', (index - 1) * Bar:GetWidth(), 0) + + ClassPower[index] = Bar + end + + -- Register with oUF + self.ClassPower = ClassPower +--]] + +local _, ns = ... +local oUF = ns.oUF + +local _, PlayerClass = UnitClass('player') + +-- sourced from FrameXML/Constants.lua +local SPELL_POWER_ENERGY = 3 +local COMBO_POINTS_ID = 4 + +-- Holds the class specific stuff. +local ClassPowerID, ClassPowerType +local ClassPowerEnable, ClassPowerDisable +local RequireSpec, RequirePower, RequireSpell + +local function UpdateColor(element, powerType) + local color = element.__owner.colors.power[powerType] + local r, g, b = color.r, color.g, color.b + + for i = 1, #element do + local bar = element[i] + bar:SetStatusBarColor(r, g, b) + + local bg = bar.bg + if(bg) then + local mu = bg.multiplier or 1 + bg:SetVertexColor(r * mu, g * mu, b * mu) + end + end + + --[[ Callback: ClassPower:PostUpdateColor(r, g, b) + Called after the element color has been updated. + + * self - the ClassPower element + * r - the red component of the used color (number)[0-1] + * g - the green component of the used color (number)[0-1] + * b - the blue component of the used color (number)[0-1] + --]] + if(element.PostUpdateColor) then + element:PostUpdateColor(r, g, b) + end +end + +local function Update(self, event, unit, powerType) + if(not (unit and (UnitIsUnit(unit, 'player') and (not powerType or powerType == ClassPowerType) + or unit == 'vehicle' and powerType == 'COMBO_POINTS'))) then + return + end + + local element = self.ClassPower + + --[[ Callback: ClassPower:PreUpdate(event) + Called before the element has been updated. + + * self - the ClassPower element + ]] + if(element.PreUpdate) then + element:PreUpdate() + end + + local cur, max, oldMax + if(event ~= 'ClassPowerDisable') then + local powerID = unit == 'vehicle' and COMBO_POINTS_ID or ClassPowerID + + powerType = powerType or ClassPowerType + + max = powerType == 'COMBO_POINTS' and GetComboPoints(unit, 'target') or UnitPower(unit, powerID) + cur = powerType == 'COMBO_POINTS' and GetComboPoints(unit, 'target') or UnitPower(unit, powerID) + + local numActive = cur + 0.9 + for i = 1, max do + if(i > numActive) then + element[i]:Hide() + element[i]:SetValue(0) + else + element[i]:Show() + element[i]:SetValue(cur - i + 1) + end + end + + oldMax = element.__max + if(max ~= oldMax) then + if(max < oldMax) then + for i = max + 1, oldMax do + element[i]:Hide() + element[i]:SetValue(0) + end + end + + element.__max = max + end + end + --[[ Callback: ClassPower:PostUpdate(cur, max, hasMaxChanged, powerType) + Called after the element has been updated. + + * self - the ClassPower element + * cur - the current amount of power (number) + * max - the maximum amount of power (number) + * hasMaxChanged - indicates whether the maximum amount has changed since the last update (boolean) + * powerType - the active power type (string) + --]] + if(element.PostUpdate) then + return element:PostUpdate(cur, max, oldMax ~= max, powerType) + end +end + +local function Path(self, ...) + --[[ Override: ClassPower.Override(self, event, unit, ...) + Used to completely override the internal update function. + + * self - the parent object + * event - the event triggering the update (string) + * unit - the unit accompanying the event (string) + * ... - the arguments accompanying the event + --]] + return (self.ClassPower.Override or Update) (self, ...) +end + +local function Visibility(self, event, unit) + local element = self.ClassPower + local shouldEnable + + if UnitHasVehicleUI('player') then + shouldEnable = true + unit = 'vehicle' + elseif(not RequirePower or RequirePower == UnitPowerType('player')) then + if(not RequireSpell or IsSpellKnown(RequireSpell)) then + self:UnregisterEvent('SPELLS_CHANGED', Visibility) + shouldEnable = true + unit = 'player' + else + self:RegisterEvent('SPELLS_CHANGED', Visibility, true) + end + end + + local isEnabled = element.__isEnabled + local powerType = unit == 'vehicle' and 'COMBO_POINTS' or ClassPowerType + + if(shouldEnable) then + --[[ Override: ClassPower:UpdateColor(powerType) + Used to completely override the internal function for updating the widgets' colors. + + * self - the ClassPower element + * powerType - the active power type (string) + --]] + (element.UpdateColor or UpdateColor) (element, powerType) + end + + if(shouldEnable and not isEnabled) then + ClassPowerEnable(self) + + --[[ Callback: ClassPower:PostVisibility(isVisible) + Called after the element's visibility has been changed. + + * self - the ClassPower element + * isVisible - the current visibility state of the element (boolean) + --]] + if(element.PostVisibility) then + element:PostVisibility(true) + end + elseif(not shouldEnable and (isEnabled or isEnabled == nil)) then + ClassPowerDisable(self) + + if(element.PostVisibility) then + element:PostVisibility(false) + end + elseif(shouldEnable and isEnabled) then + Path(self, event, unit, powerType) + end +end + +local function VisibilityPath(self, ...) + --[[ Override: ClassPower.OverrideVisibility(self, event, unit) + Used to completely override the internal visibility function. + + * self - the parent object + * event - the event triggering the update (string) + * unit - the unit accompanying the event (string) + --]] + return (self.ClassPower.OverrideVisibility or Visibility) (self, ...) +end + +local function ForceUpdate(element) + return VisibilityPath(element.__owner, 'ForceUpdate', element.__owner.unit) +end + +do + function ClassPowerEnable(self) + self:RegisterEvent('UNIT_MAXPOWER', Path) + self:RegisterEvent('UNIT_POWER_UPDATE', Path) + self:RegisterEvent('PLAYER_TARGET_CHANGED', VisibilityPath, true) + + self.ClassPower.__isEnabled = true + + if UnitHasVehicleUI('player') then + Path(self, 'ClassPowerEnable', 'vehicle', 'COMBO_POINTS') + end + end + + function ClassPowerDisable(self) + self:UnregisterEvent('UNIT_POWER_UPDATE', Path) + self:UnregisterEvent('UNIT_MAXPOWER', Path) + self:UnregisterEvent('PLAYER_TARGET_CHANGED', VisibilityPath) + + local element = self.ClassPower + for i = 1, #element do + element[i]:Hide() + end + + element.__isEnabled = false + Path(self, 'ClassPowerDisable', 'player', ClassPowerType) + end + + if(PlayerClass == 'ROGUE' or PlayerClass == 'DRUID' or PlayerClass == 'HERO') then + ClassPowerType = 'COMBO_POINTS' + + if(PlayerClass == 'DRUID') then + RequirePower = SPELL_POWER_ENERGY + RequireSpell = 768 -- cat form + end + end +end + +local function Enable(self, unit) + local element = self.ClassPower + if(element and UnitIsUnit(unit, 'player')) then + element.__owner = self + element.__max = #element + element.ForceUpdate = ForceUpdate + + if (RequireSpell and not IsSpellKnown(RequireSpell)) then + self:RegisterEvent('SPELLS_CHANGED', VisibilityPath, true) + end + + if(RequirePower) then + self:RegisterEvent('UNIT_DISPLAYPOWER', VisibilityPath) + end + self:RegisterEvent('UNIT_COMBO_POINTS', VisibilityPath) + + + element.ClassPowerEnable = ClassPowerEnable + element.ClassPowerDisable = ClassPowerDisable + + for i = 1, #element do + local bar = element[i] + if bar:IsObjectType('StatusBar') then + if not bar:GetStatusBarTexture() then + bar:SetStatusBarTexture([[Interface\TargetingFrame\UI-StatusBar]]) + end + + bar:SetMinMaxValues(0, 1) + end + end + + return true + end +end + +local function Disable(self) + if(self.ClassPower) then + ClassPowerDisable(self) + self:UnregisterEvent('UNIT_DISPLAYPOWER', VisibilityPath) + self:UnregisterEvent('SPELLS_CHANGED', Visibility) + self:RegisterEvent('UNIT_COMBO_POINTS', VisibilityPath) + end +end + +oUF:AddElement('ClassPower', VisibilityPath, Enable, Disable) \ No newline at end of file diff --git a/ElvUI/Libraries/oUF/oUF.xml b/ElvUI/Libraries/oUF/oUF.xml index a2a94a3..88e4a04 100644 --- a/ElvUI/Libraries/oUF/oUF.xml +++ b/ElvUI/Libraries/oUF/oUF.xml @@ -33,6 +33,7 @@