From 6d54612877aa9b09fe3df80976e48e0af83d9cde Mon Sep 17 00:00:00 2001
From: andrew6180 <16847730+andrew6180@users.noreply.github.com>
Date: Thu, 1 Jun 2023 14:40:10 -0700
Subject: [PATCH] Modules/Nameplates: Fix class power module
---
ElvUI/Libraries/oUF/elements/classpower.lua | 313 ++++++++++++++++++
ElvUI/Libraries/oUF/oUF.xml | 1 +
.../Nameplates/Elements/ClassPower.lua | 6 +-
ElvUI/Settings/Profile.lua | 18 +-
ElvUI_OptionsUI/Core.lua | 4 +-
ElvUI_OptionsUI/Nameplates.lua | 4 +-
6 files changed, 331 insertions(+), 15 deletions(-)
create mode 100644 ElvUI/Libraries/oUF/elements/classpower.lua
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 @@
+