--[[ # Element: Health Bar Handles the updating of a status bar that displays the unit's health. ## Widget Health - A `StatusBar` used to represent the unit's health. ## 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 set. ## Options .frequentUpdates - Indicates whether to use OnUpdate script instead of UNIT_HEALTH to update the bar (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) .colorHappiness - Use `self.colors.happiness` to color the bar if the unit is pet based on pet happiness (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) .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) .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 health percentage (boolean) .colorHealth - Use `self.colors.health` to color the bar. This flag is used to reset the bar color back to default if none of the above conditions are met (boolean) ## Sub-Widgets Options .multiplier - Used to tint the background based on the main widgets R, G and B values. Defaults to 1 (number)[0-1] ## Attributes .disconnected - Indicates whether the unit is disconnected (boolean) ## Examples -- Position and size local Health = CreateFrame('StatusBar', nil, self) Health:SetHeight(20) Health:SetPoint('TOP') Health:SetPoint('LEFT') Health:SetPoint('RIGHT') -- Add a background local Background = Health:CreateTexture(nil, 'BACKGROUND') Background:SetAllPoints(Health) Background:SetTexture(1, 1, 1, .5) -- Options Health.frequentUpdates = true Health.colorTapping = true Health.colorDisconnected = true Health.colorClass = true Health.colorReaction = true Health.colorHealth = true -- Make the background darker. Background.multiplier = .5 -- Register it with oUF Health.bg = Background self.Health = Health --]] local _, ns = ... local oUF = ns.oUF local Private = oUF.Private local function UpdateColor(self, event, unit) if(not unit or self.unit ~= unit) then return end local element = self.Health local r, g, b, t if(element.colorDisconnected and element.disconnected) then t = self.colors.disconnected elseif(element.colorTapping and not UnitPlayerControlled(unit) and (UnitIsTapped(unit) and not UnitIsTappedByPlayer(unit) and not UnitIsTappedByAllThreatList(unit))) then t = self.colors.tapped elseif(element.colorHappiness and UnitIsUnit(unit, 'pet') and GetPetHappiness()) then t = self.colors.happiness[GetPetHappiness()] elseif(element.colorThreat and not UnitPlayerControlled(unit) and UnitThreatSituation('player', unit)) then t = self.colors.threat[UnitThreatSituation('player', unit)] elseif(element.colorClass and UnitIsPlayer(unit)) or (element.colorClassNPC and not UnitIsPlayer(unit)) or (element.colorClassPet and UnitPlayerControlled(unit) and not UnitIsPlayer(unit)) then if UnitIsUnit("player", unit) then t = oUF.herocolor else local _, class = UnitClass(unit) t = self.colors.class[class] end elseif(element.colorReaction and UnitReaction(unit, 'player')) then t = self.colors.reaction[UnitReaction(unit, 'player')] elseif(element.colorSmooth) then r, g, b = self:ColorGradient(element.cur or 1, element.max or 1, unpack(element.smoothGradient or self.colors.smooth)) elseif(element.colorHealth) then t = self.colors.health end if(t) then r, g, b = t[1], t[2], t[3] end if(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 if(element.PostUpdateColor) then element:PostUpdateColor(unit, r, g, b) end end local function ColorPath(self, ...) --[[ Override: Health.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.Health.UpdateColor or UpdateColor) (self, ...) end local function Update(self, event, unit) if(not unit or self.unit ~= unit) then return end local element = self.Health --[[ Callback: Health:PreUpdate(unit) Called before the element has been updated. * self - the Health element * unit - the unit for which the update has been triggered (string) --]] if(element.PreUpdate) then element:PreUpdate(unit) end local cur, max = UnitHealth(unit), UnitHealthMax(unit) local disconnected = not UnitIsConnected(unit) element:SetMinMaxValues(0, max) if(disconnected) then element:SetValue(max) else if(cur == 0) then cur = 0.0001 end element:SetValue(cur) end element.cur = cur element.max = max element.disconnected = disconnected --[[ Callback: Health:PostUpdate(unit, cur, max) Called after the element has been updated. * self - the Health element * unit - the unit for which the update has been triggered (string) * cur - the unit's current health value (number) * max - the unit's maximum possible health value (number) --]] if(element.PostUpdate) then element:PostUpdate(unit, cur, max) end end local function Path(self, ...) --[[ Override: Health.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) --]] (self.Health.Override or Update) (self, ...); ColorPath(self, ...) end local function ForceUpdate(element) Path(element.__owner, 'ForceUpdate', element.__owner.unit) end --[[ Health:SetColorDisconnected(state) Used to toggle coloring if the unit is offline. * self - the Health element * state - the desired state (boolean) --]] local function SetColorDisconnected(element, state) if(element.colorDisconnected ~= state) then element.colorDisconnected = state if(element.colorDisconnected) then element.__owner:RegisterEvent('UNIT_CONNECTION', ColorPath) else element.__owner:UnregisterEvent('UNIT_CONNECTION', ColorPath) end end end --[[ Health:SetColorHappiness(state) Used to toggle coloring by the unit's happiness. * self - the Health element * state - the desired state (boolean) --]] local function SetColorHappiness(element, state) if(element.colorHappiness ~= state) then element.colorHappiness = state if(element.colorHappiness) then element.__owner:RegisterEvent('UNIT_HAPPINESS', ColorPath) else element.__owner:UnregisterEvent('UNIT_HAPPINESS', ColorPath) end end end --[[ Health:SetColorSelection(state, isForced) Used to toggle coloring by the unit's selection. * self - the Health 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 --[[ Health:SetColorTapping(state) Used to toggle coloring if the unit isn't tapped by the player. * self - the Health element * state - the desired state (boolean) --]] local function SetColorTapping(element, state) if(element.colorTapping ~= state) then element.colorTapping = state if(element.colorTapping) then element.__owner:RegisterEvent('UNIT_FACTION', ColorPath) else element.__owner:UnregisterEvent('UNIT_FACTION', ColorPath) end end end --[[ Health:SetColorThreat(state) Used to toggle coloring by the unit's threat status. * self - the Health element * state - the desired state (boolean) --]] local function SetColorThreat(element, state) if(element.colorThreat ~= state) then element.colorThreat = state if(element.colorThreat) then element.__owner:RegisterEvent('UNIT_THREAT_LIST_UPDATE', ColorPath) else element.__owner:UnregisterEvent('UNIT_THREAT_LIST_UPDATE', ColorPath) end end end local function onHealthUpdate(self) if(self.disconnected) then return end local unit = self.__owner.unit local health = UnitHealth(unit) if(health ~= self.health) then self.health = health return Path(self.__owner, 'OnHealthUpdate', unit) end end --[[ Health:SetFrequentUpdates(state) Used to toggle frequent updates. * self - the Health element * state - the desired state (boolean) --]] local function SetFrequentUpdates(element, state) if(element.frequentUpdates ~= state) then element.frequentUpdates = state if(element.frequentUpdates) then element:SetScript('OnUpdate', onHealthUpdate) local unit = element.__owner.unit if((unit == 'party' or unit:match('party%d?$')) and not element:IsEventRegistered("UNIT_HEALTH")) then element:RegisterEvent('UNIT_HEALTH', Path) elseif(element:IsEventRegistered('UNIT_HEALTH')) then element:UnregisterEvent('UNIT_HEALTH', Path) end else element:SetScript('OnUpdate', nil) element.__owner:RegisterEvent('UNIT_HEALTH', Path) end end end local function Enable(self, unit) local element = self.Health if(element) then element.__owner = self element.ForceUpdate = ForceUpdate element.SetColorDisconnected = SetColorDisconnected element.SetColorSelection = SetColorSelection element.SetColorHappiness = SetColorHappiness element.SetColorTapping = SetColorTapping element.SetColorThreat = SetColorThreat element.SetFrequentUpdates = SetFrequentUpdates if(element.colorDisconnected) then self:RegisterEvent('UNIT_CONNECTION', ColorPath) end if(element.colorHappiness) then self:RegisterEvent('UNIT_HAPPINESS', ColorPath) end if(element.colorTapping) then self:RegisterEvent('UNIT_FACTION', ColorPath) end if(element.colorThreat) then self:RegisterEvent('UNIT_THREAT_LIST_UPDATE', ColorPath) end if(element.frequentUpdates and (unit and not unit:match('%w+target$'))) then element:SetScript('OnUpdate', onHealthUpdate) -- The party frames need this to handle disconnect states correctly. if(unit == 'party') then self:RegisterEvent('UNIT_HEALTH', Path) end else self:RegisterEvent('UNIT_HEALTH', Path) end self:RegisterEvent('UNIT_MAXHEALTH', Path) if(element:IsObjectType('StatusBar') and not element:GetStatusBarTexture()) then element:SetStatusBarTexture([[Interface\TargetingFrame\UI-StatusBar]]) end element:Show() return true end end local function Disable(self) local element = self.Health if(element) then element:Hide() if(element:GetScript('OnUpdate')) then element:SetScript('OnUpdate', nil) end self:UnregisterEvent('UNIT_HEALTH', Path) self:UnregisterEvent('UNIT_MAXHEALTH', Path) self:UnregisterEvent('UNIT_CONNECTION', ColorPath) self:UnregisterEvent('UNIT_FACTION', ColorPath) self:UnregisterEvent('UNIT_HAPPINESS', ColorPath) self:UnregisterEvent('UNIT_THREAT_LIST_UPDATE', ColorPath) self:UnregisterEvent(')UNIT_THREAT_SITUATION_UPDATE', ColorPath) end end oUF:AddElement('Health', Path, Enable, Disable)