2020-11-13 14:27:50 -05:00

482 lines
16 KiB
Lua

--[[
# Element: Power Bar
Handles the updating of a status bar that displays the unit's power.
## Widget
Power - A `StatusBar` used to represent the unit's power.
## Sub-Widgets
.bg - A `Texture` used as a background. It will inherit the color of the main StatusBar.
## Notes
A default texture will be applied if the widget is a StatusBar and doesn't have a texture or a color set.
## Options
.frequentUpdates - Indicates whether to use UNIT_POWER_FREQUENT instead UNIT_POWER_UPDATE to update the
bar (boolean)
.displayAltPower - Use this to let the widget display alternative power, if the unit has one.
By default, it does so only for raid and party units. If none, the display will fall
back to the primary power (boolean)
.useAtlas - Use this to let the widget use an atlas for its texture if an atlas is present in
`self.colors.power` for the appropriate power type (boolean)
.smoothGradient - 9 color values to be used with the .colorSmooth option (table)
.considerSelectionInCombatHostile - Indicates whether selection should be considered hostile while the unit is in
combat with the player (boolean)
The following options are listed by priority. The first check that returns true decides the color of the bar.
.colorDisconnected - Use `self.colors.disconnected` to color the bar if the unit is offline (boolean)
.colorTapping - Use `self.colors.tapping` to color the bar if the unit isn't tapped by the player (boolean)
.colorThreat - Use `self.colors.threat[threat]` to color the bar based on the unit's threat status. `threat` is
defined by the first return of [UnitThreatSituation](https://wow.gamepedia.com/API_UnitThreatSituation) (boolean)
.colorPower - Use `self.colors.power[token]` to color the bar based on the unit's power type. This method will
fall-back to `:GetAlternativeColor()` if it can't find a color matching the token. If this function
isn't defined, then it will attempt to color based upon the alternative power colors returned by
[UnitPowerType](http://wowprogramming.com/docs/api/UnitPowerType.html). If these aren't
defined, then it will attempt to color the bar based upon `self.colors.power[type]`. In case of
failure it'll default to `self.colors.power.MANA` (boolean)
.colorClass - Use `self.colors.class[class]` to color the bar based on unit class. `class` is defined by the
second return of [UnitClass](http://wowprogramming.com/docs/api/UnitClass.html) (boolean)
.colorClassNPC - Use `self.colors.class[class]` to color the bar if the unit is a NPC (boolean)
.colorClassPet - Use `self.colors.class[class]` to color the bar if the unit is player controlled, but not a player
(boolean)
.colorSelection - Use `self.colors.selection[selection]` to color the bar based on the unit's selection color.
`selection` is defined by the return value of Private.unitSelectionType, a wrapper function
for [UnitSelectionType](https://wow.gamepedia.com/API_UnitSelectionType) (boolean)
.colorReaction - Use `self.colors.reaction[reaction]` to color the bar based on the player's reaction towards the
unit. `reaction` is defined by the return value of
[UnitReaction](http://wowprogramming.com/docs/api/UnitReaction.html) (boolean)
.colorSmooth - Use `smoothGradient` if present or `self.colors.smooth` to color the bar with a smooth gradient
based on the player's current power percentage (boolean)
## Sub-Widget Options
.multiplier - A multiplier used to tint the background based on the main widgets R, G and B values. Defaults to 1
(number)[0-1]
## Examples
-- Position and size
local Power = CreateFrame('StatusBar', nil, self)
Power:SetHeight(20)
Power:SetPoint('BOTTOM')
Power:SetPoint('LEFT')
Power:SetPoint('RIGHT')
-- Add a background
local Background = Power:CreateTexture(nil, 'BACKGROUND')
Background:SetAllPoints(Power)
Background:SetTexture(1, 1, 1, .5)
-- Options
Power.frequentUpdates = true
Power.colorTapping = true
Power.colorDisconnected = true
Power.colorPower = true
Power.colorClass = true
Power.colorReaction = true
-- Make the background darker.
Background.multiplier = .5
-- Register it with oUF
Power.bg = Background
self.Power = Power
--]]
local _, ns = ...
local oUF = ns.oUF
local Private = oUF.Private
local unitSelectionType = Private.unitSelectionType
-- sourced from FrameXML/UnitPowerBarAlt.lua
local ALTERNATE_POWER_INDEX = Enum.PowerType.Alternate or 10
--[[ Override: Power:GetDisplayPower()
Used to get info on the unit's alternative power, if any.
Should return the power type index (see [Enum.PowerType.Alternate](https://wow.gamepedia.com/Enum_Unit.PowerType))
and the minimum value for the given power type (see [info.minPower](https://wow.gamepedia.com/API_GetUnitPowerBarInfo))
or nil if the unit has no alternative (alternate) power or it should not be
displayed. In case of a nil return, the element defaults to the primary power
type and zero for the minimum value.
* self - the Power element
--]]
local function GetDisplayPower(element)
local unit = element.__owner.unit
local barInfo = GetUnitPowerBarInfo(unit)
if(barInfo and barInfo.showOnRaid and (UnitInParty(unit) or UnitInRaid(unit))) then
return ALTERNATE_POWER_INDEX, barInfo.minPower
end
end
local function UpdateColor(self, event, unit)
if(self.unit ~= unit) then return end
local element = self.Power
local pType, pToken, altR, altG, altB = UnitPowerType(unit)
local r, g, b, t, atlas
if(element.colorDisconnected and not UnitIsConnected(unit)) then
t = self.colors.disconnected
elseif(element.colorTapping and not UnitPlayerControlled(unit) and UnitIsTapDenied(unit)) then
t = self.colors.tapped
elseif(element.colorThreat and not UnitPlayerControlled(unit) and UnitThreatSituation('player', unit)) then
t = self.colors.threat[UnitThreatSituation('player', unit)]
elseif(element.colorPower) then
if(element.displayType ~= ALTERNATE_POWER_INDEX) then
t = self.colors.power[pToken]
if(not t) then
if(element.GetAlternativeColor) then
r, g, b = element:GetAlternativeColor(unit, pType, pToken, altR, altG, altB)
elseif(altR) then
r, g, b = altR, altG, altB
if(r > 1 or g > 1 or b > 1) then
-- BUG: As of 7.0.3, altR, altG, altB may be in 0-1 or 0-255 range.
r, g, b = r / 255, g / 255, b / 255
end
else
t = self.colors.power[pType] or self.colors.power.MANA
end
end
else
t = self.colors.power[ALTERNATE_POWER_INDEX]
end
if(element.useAtlas and t and t.atlas) then
atlas = t.atlas
end
elseif(element.colorClass and UnitIsPlayer(unit))
or (element.colorClassNPC and not UnitIsPlayer(unit))
or (element.colorClassPet and UnitPlayerControlled(unit) and not UnitIsPlayer(unit)) then
local _, class = UnitClass(unit)
t = self.colors.class[class]
elseif(element.colorSelection and unitSelectionType(unit, element.considerSelectionInCombatHostile)) then
t = self.colors.selection[unitSelectionType(unit, element.considerSelectionInCombatHostile)]
elseif(element.colorReaction and UnitReaction(unit, 'player')) then
t = self.colors.reaction[UnitReaction(unit, 'player')]
elseif(element.colorSmooth) then
local adjust = 0 - (element.min or 0)
r, g, b = self:ColorGradient((element.cur or 1) + adjust, (element.max or 1) + adjust, unpack(element.smoothGradient or self.colors.smooth))
end
if(t) then
r, g, b = t[1], t[2], t[3]
end
if(atlas) then
element:SetStatusBarAtlas(atlas)
element:SetStatusBarColor(1, 1, 1)
elseif(b) then
element:SetStatusBarColor(r, g, b)
local bg = element.bg
if(bg) then
local mu = bg.multiplier or 1
bg:SetVertexColor(r * mu, g * mu, b * mu)
end
end
--[[ Callback: Power:PostUpdateColor(unit, r, g, b)
Called after the element color has been updated.
local bg = element.bg
if(bg and b) then
local mu = bg.multiplier or 1
bg:SetVertexColor(r * mu, g * mu, b * mu)
end
--]]
if(element.PostUpdateColor) then
element:PostUpdateColor(unit, r, g, b)
end
end
local function ColorPath(self, ...)
--[[ Override: Power.UpdateColor(self, event, unit)
Used to completely override the internal function for updating the widgets' colors.
* self - the parent object
* event - the event triggering the update (string)
* unit - the unit accompanying the event (string)
--]]
(self.Power.UpdateColor or UpdateColor) (self, ...)
end
local function Update(self, event, unit)
if(self.unit ~= unit) then return end
local element = self.Power
--[[ Callback: Power:PreUpdate(unit)
Called before the element has been updated.
* self - the Power element
* unit - the unit for which the update has been triggered (string)
--]]
if(element.PreUpdate) then
element:PreUpdate(unit)
end
local displayType, min
if(element.displayAltPower) then
displayType, min = element:GetDisplayPower()
end
local cur, max = UnitPower(unit, displayType), UnitPowerMax(unit, displayType)
if not min then min = 0 end
element:SetMinMaxValues(min, max)
if(UnitIsConnected(unit)) then
element:SetValue(cur)
else
element:SetValue(max)
end
element.cur = cur
element.min = min
element.max = max
element.displayType = displayType
--[[ Callback: Power:PostUpdate(unit, cur, min, max)
Called after the element has been updated.
* self - the Power element
* unit - the unit for which the update has been triggered (string)
* cur - the unit's current power value (number)
* min - the unit's minimum possible power value (number)
* max - the unit's maximum possible power value (number)
--]]
if(element.PostUpdate) then
element:PostUpdate(unit, cur, min, max)
end
end
local function Path(self, event, ...)
if (self.isForced and event ~= 'ElvUI_UpdateAllElements') then return end -- ElvUI changed
--[[ Override: Power.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
--]]
(self.Power.Override or Update) (self, event, ...);
ColorPath(self, event, ...)
end
local function ForceUpdate(element)
Path(element.__owner, 'ForceUpdate', element.__owner.unit)
end
--[[ Power:SetColorDisconnected(state, isForced)
Used to toggle coloring if the unit is offline.
* self - the Power element
* state - the desired state (boolean)
* isForced - forces the event update even if the state wasn't changed (boolean)
--]]
local function SetColorDisconnected(element, state, isForced)
if(element.colorDisconnected ~= state or isForced) then
element.colorDisconnected = state
if(state) then
element.__owner:RegisterEvent('UNIT_CONNECTION', ColorPath)
else
element.__owner:UnregisterEvent('UNIT_CONNECTION', ColorPath)
end
end
end
--[[ Power:SetColorSelection(state, isForced)
Used to toggle coloring by the unit's selection.
* self - the Power element
* state - the desired state (boolean)
* isForced - forces the event update even if the state wasn't changed (boolean)
--]]
local function SetColorSelection(element, state, isForced)
if(element.colorSelection ~= state or isForced) then
element.colorSelection = state
if(state) then
element.__owner:RegisterEvent('UNIT_FLAGS', ColorPath)
else
element.__owner:UnregisterEvent('UNIT_FLAGS', ColorPath)
end
end
end
--[[ Power:SetColorTapping(state, isForced)
Used to toggle coloring if the unit isn't tapped by the player.
* self - the Power element
* state - the desired state (boolean)
* isForced - forces the event update even if the state wasn't changed (boolean)
--]]
local function SetColorTapping(element, state, isForced)
if(element.colorTapping ~= state or isForced) then
element.colorTapping = state
if(state) then
element.__owner:RegisterEvent('UNIT_FACTION', ColorPath)
else
element.__owner:UnregisterEvent('UNIT_FACTION', ColorPath)
end
end
end
--[[ Power:SetColorThreat(state, isForced)
Used to toggle coloring by the unit's threat status.
* self - the Power element
* state - the desired state (boolean)
* isForced - forces the event update even if the state wasn't changed (boolean)
--]]
local function SetColorThreat(element, state, isForced)
if(element.colorThreat ~= state or isForced) then
element.colorThreat = state
if(state) then
element.__owner:RegisterEvent('UNIT_THREAT_LIST_UPDATE', ColorPath)
else
element.__owner:UnregisterEvent('UNIT_THREAT_LIST_UPDATE', ColorPath)
end
end
end
--[[ Power:SetFrequentUpdates(state, isForced)
Used to toggle frequent updates.
* self - the Power element
* state - the desired state (boolean)
* isForced - forces the event update even if the state wasn't changed (boolean)
--]]
local function SetFrequentUpdates(element, state, isForced)
if(element.frequentUpdates ~= state or isForced) then
element.frequentUpdates = state
if(state) then
element.__owner:UnregisterEvent('UNIT_POWER_UPDATE', Path)
element.__owner:RegisterEvent('UNIT_POWER_FREQUENT', Path)
else
element.__owner:UnregisterEvent('UNIT_POWER_FREQUENT', Path)
element.__owner:RegisterEvent('UNIT_POWER_UPDATE', Path)
end
end
end
-- ElvUI changed block
local onUpdateElapsed, onUpdateWait = 0, 0.25
local function onUpdatePower(self, elapsed)
if onUpdateElapsed > onUpdateWait then
Path(self.__owner, 'OnUpdate', self.__owner.unit)
onUpdateElapsed = 0
else
onUpdateElapsed = onUpdateElapsed + elapsed
end
end
local function SetPowerUpdateSpeed(self, state)
onUpdateWait = state
end
local function SetPowerUpdateMethod(self, state, force)
if self.effectivePower ~= state or force then
self.effectivePower = state
if state then
self.Power:SetScript('OnUpdate', onUpdatePower)
self:UnregisterEvent('UNIT_POWER_FREQUENT', Path)
self:UnregisterEvent('UNIT_POWER_UPDATE', Path)
self:UnregisterEvent('UNIT_MAXPOWER', Path)
else
self.Power:SetScript('OnUpdate', nil)
self:RegisterEvent('UNIT_MAXPOWER', Path)
if self.Power.frequentUpdates then
self:RegisterEvent('UNIT_POWER_FREQUENT', Path)
else
self:RegisterEvent('UNIT_POWER_UPDATE', Path)
end
end
end
end
-- end block
local function Enable(self)
local element = self.Power
if(element) then
element.__owner = self
element.ForceUpdate = ForceUpdate
element.SetColorDisconnected = SetColorDisconnected
element.SetColorSelection = SetColorSelection
element.SetColorTapping = SetColorTapping
element.SetColorThreat = SetColorThreat
element.SetFrequentUpdates = SetFrequentUpdates
-- ElvUI changed block
self.SetPowerUpdateSpeed = SetPowerUpdateSpeed
self.SetPowerUpdateMethod = SetPowerUpdateMethod
SetPowerUpdateMethod(self, self.effectivePower, true)
-- end block
if(element.colorDisconnected) then
self:RegisterEvent('UNIT_CONNECTION', ColorPath)
end
if(element.colorSelection) then
self:RegisterEvent('UNIT_FLAGS', ColorPath)
end
if(element.colorTapping) then
self:RegisterEvent('UNIT_FACTION', ColorPath)
end
if(element.colorThreat) then
self:RegisterEvent('UNIT_THREAT_LIST_UPDATE', ColorPath)
end
self:RegisterEvent('UNIT_DISPLAYPOWER', Path)
self:RegisterEvent('UNIT_POWER_BAR_HIDE', Path)
self:RegisterEvent('UNIT_POWER_BAR_SHOW', Path)
if(element:IsObjectType('StatusBar') and not (element:GetStatusBarTexture() or element:GetStatusBarAtlas())) then
element:SetStatusBarTexture([[Interface\TargetingFrame\UI-StatusBar]])
end
if(not element.GetDisplayPower) then
element.GetDisplayPower = GetDisplayPower
end
element:Show()
return true
end
end
local function Disable(self)
local element = self.Power
if(element) then
element:Hide()
element:SetScript('OnUpdate', nil) -- ElvUI changed
self:UnregisterEvent('UNIT_DISPLAYPOWER', Path)
self:UnregisterEvent('UNIT_MAXPOWER', Path)
self:UnregisterEvent('UNIT_POWER_BAR_HIDE', Path)
self:UnregisterEvent('UNIT_POWER_BAR_SHOW', Path)
self:UnregisterEvent('UNIT_POWER_FREQUENT', Path)
self:UnregisterEvent('UNIT_POWER_UPDATE', Path)
self:UnregisterEvent('UNIT_CONNECTION', ColorPath)
self:UnregisterEvent('UNIT_FACTION', ColorPath)
self:UnregisterEvent('UNIT_FLAGS', ColorPath)
self:UnregisterEvent('UNIT_THREAT_LIST_UPDATE', ColorPath)
end
end
oUF:AddElement('Power', Path, Enable, Disable)